Drive a live Pharo image over HTTP from a coding agent or shell.
Postern exposes a running Pharo image as an HTTP server. Clients send
Smalltalk expressions to /repl, read /help from the live image, and
drive compile, test, inspect, and commit loops without going through the
Pharo GUI. It is intended for local development, CI, and agent-driven
workflows where the image itself is the runtime.
Platforms: macOS, Linux (x86_64, arm64/aarch64), Windows (x86_64; ARM64 via x86_64 Stack VM emulation, headless-only) (Pharo 12)
git clone https://github.com/punt-labs/postern.git
cd postern
make
make setup
make startmake setup downloads the matching Pharo image and VM for the current
host. If Pharo cannot write to its usual user config directories
(for example inside a Codex or Claude Code sandbox), Postern falls back
to a repo-local runtime home under .tmp/pharo-home.
On Windows ARM64, make setup automatically uses the Windows x86_64
Stack VM under emulation because the default Windows JIT VM is not
reliable there. That path is headless-only, so use make start-headless
instead of make start.
On headless Linux sessions without DISPLAY or WAYLAND_DISPLAY, use
make start-headless.
If port 8422 is already in use, override it on the command line, for
example make PORT=8432 start-headless.
curl -s http://localhost:8422/health
curl -s http://localhost:8422/helpBefore exposing Postern beyond your machine or handing it to an agent, read Security.
- HTTP REPL — Evaluate Smalltalk in a live image through
POST /repl. - Live help from the image —
/helpdocuments the loaded packages, conventions, and workflow directly from the running image. - GUI and headless startup —
make startlaunches the Pharo UI when the selected VM supports it, andmake start-headlessruns without it. Windows ARM64 currently uses a headless-only Stack VM path. - Dashboard —
PosternDashboardshows live request traffic, status, and request and response bodies. - Iceberg sync helper — Repairs Iceberg reference-commit drift after CLI Git operations.
- Optional token auth — Loopback mode can run without auth; public binding always requires a token.
$ curl -s http://localhost:8422/health
ok
$ make status
alive -- <class-count> Postern classes loaded
$ make test
Tests: <run-count> Passed: <pass-count> Failures: 0 Errors: 0
The dashboard after driving Postern through make test and related
/repl activity.
| Route | Method | Description |
|---|---|---|
/repl |
POST |
Evaluate Smalltalk from a text/plain body and return the printString of the result. |
/health |
GET |
Return ok. No auth required. |
/help |
GET |
Return the table of contents for the live help. No auth required. |
/help/{section} |
GET |
Return a specific help section: api, browse, pharo, dispatch, lint, git, testing, safety, makefile, or lessons. |
| Method | Binding | Auth |
|---|---|---|
PosternServer startOn: 8422 |
loopback | none |
PosternServer startOn: 8422 withAuth: true |
loopback | required |
PosternServer startPublicOn: 8422 |
all interfaces | required |
There is no unauthenticated public mode. When auth is enabled, /repl
requires an X-Eval-Token header. The token is written to
.tmp/eval-token, and clients should read that file and send the header
explicitly. /health and /help are always unauthenticated.
Send LF line endings in requests. The server converts them to CR before
passing source to the Pharo compiler. /help responses use LF.
Open from the World menu: Postern > Dashboard, or evaluate:
PosternDashboard open.The dashboard shows live traffic per server: method, path, status code, duration, and body snippet. The filter field takes keyboard focus on open. Multiple windows can monitor different servers simultaneously.
After CLI Git operations, Iceberg's working copy reference commit can
drift from filesystem HEAD, showing a false dirty state. Fix it from
the Iceberg browser with Postern > Sync reference commit, or
evaluate:
repo workingCopy referenceCommit: repo head commit.Metacello new
baseline: 'Postern';
repository: 'github://punt-labs/postern:main';
load.Load including tests:
Metacello new
baseline: 'Postern';
repository: 'github://punt-labs/postern:main';
load: 'all'.External agents should read the live help before writing Smalltalk:
curl -s http://localhost:8422/help
curl -s http://localhost:8422/help/api
curl -s http://localhost:8422/help/browse
curl -s http://localhost:8422/help/pharo
curl -s http://localhost:8422/help/safetyThe /help endpoint is served by the running image, so it reflects the
loaded packages and current conventions instead of a static doc set.
| Section | What it documents |
|---|---|
/help/api |
Eval server protocol: endpoints, auth, request and response format |
/help/browse |
Read-only introspection: classes, methods, senders, implementors, source lookup |
/help/pharo |
Pharo 12 standards: fluid class syntax, protocols, JSON pattern |
/help/dispatch |
Sub-agent delegation: TDD cycle, required spec elements, anti-patterns |
/help/lint |
Lint discipline: make lint is the gate, not per-method critiques |
/help/git |
Iceberg commits, CLI merges, reference commit sync |
/help/testing |
Scoped test runs, never the full Pharo suite |
/help/safety |
Deadlock prevention, image discipline, orphan recovery |
/help/makefile |
Image lifecycle: setup, start, stop, rebuild |
/help/lessons |
Incident record: cascades, process failures, scope safety |
For Codex users, this repo also includes .codex/config.toml and
codex/rules/default.rules so localhost help and REPL access can be
preconfigured after the repo is trusted once. make setup already
handles the common sandbox case by falling back to .tmp/pharo-home
when the normal Pharo config directories are not writable.
Postern accepts Smalltalk code over HTTP and runs it in the live image.
A client that can successfully call /repl can do what normal Smalltalk
code in that image can do: change classes and methods, inspect objects,
read and write files, and run shell commands with the OS permissions of
the user running Pharo.
By default, make start and make start-headless bind to localhost
only and do not enable auth. When auth is enabled, the
X-Eval-Token is a shared secret for /repl; it is not a sandbox,
permission system, or read-only mode. /help and /health remain
unauthenticated even when auth is on.
Do not expose Postern to untrusted networks or untrusted agents. Do not run it in an environment containing secrets or data you would not hand to arbitrary code running as your user account.
Pharo stores code in a live object graph called an image. Compiling a method installs it into that graph immediately. The same session can define a class, call a method on it, run its tests, and inspect the objects it creates without a process restart.
For agent-driven coding, that means the loop stays inside the running system. After sending a compile request, the agent can call the new method in the next request and branch on the real runtime result, not a restarted subprocess. Iceberg runs inside the same image, so commit operations can stay in the same session too.
An external agent can drive a complete edit cycle through HTTP: read the live help, define or compile code, run scoped tests, and commit through Iceberg. A typical sequence looks like this:
curl -s http://localhost:8422/help/api
curl -s http://localhost:8422/help/pharocurl -s -X POST http://localhost:8422/repl \
-H "Content-Type: text/plain" \
-d "(Object << #Counter
slots: { #count };
package: 'MyPackage') install"curl -s -X POST http://localhost:8422/repl \
-H "Content-Type: text/plain" \
-d "Counter compile: 'increment
count := (count ifNil: [ 0 ]) + 1' classified: 'actions'"curl -s -X POST http://localhost:8422/repl \
-H "Content-Type: text/plain" \
-d "CounterTest buildSuite run"curl -s -X POST http://localhost:8422/repl \
-H "Content-Type: text/plain" \
-d "| repo |
repo := IceRepository registry detect: [ :r | r name = 'my-project' ].
repo workingCopy refreshDirtyPackages.
repo workingCopy commitWithMessage: 'feat(counter): add increment'"| Package | Contents |
|---|---|
Postern-Core |
PosternServer, PosternDelegate, PosternHelp, PosternImageBrowser, PosternWidget |
Postern-Dashboard |
PosternDashboard, PosternRequestLogger, PosternDashboardModel |
Postern-IcebergExtensions |
PosternSyncReferenceCommitCommand for Iceberg reference-commit sync |
The name is intentional: a postern is a back door or gate, a private side entrance, or in fortification usage, a small secondary gate in a wall or castle. That fits this project literally: it gives tools a side entrance into a live Pharo image without making the GUI the only way in.
make rebuild # fresh image from Tonel (proves source completeness)
make test # run all Postern tests
make lint # Renraku lint — zero non-clean lines required before commit
make filein # reload Tonel packages into a running image
make eval # interactive Smalltalk eval (stdin to eval server)
make transcript # read the Pharo Transcript
make status # health check and loaded-class count
make stop # kill Pharo without saving the imageThe image is disposable. All code lives in src/ in Tonel format.
make rebuild must always succeed; if it fails, the source files are
incomplete.
MIT — see LICENSE.
