Skip to content
Local validation

Local validation

FORGE only works behind CORE, so validating it locally needs a stand-in for CORE’s proxy. The two affordances on this page are for local validation only. No deployment uses either of them.

forge dev proxy

forge dev proxy --email <addr> [--listen 127.0.0.1:8081] [--upstream http://127.0.0.1:8080] [--connections <dir>]

It impersonates CORE. The proxy:

  • serves /forge/… and strips the prefix before proxying to --upstream. It also redirects / to /forge/;
  • drops any inbound X-Core-Identity and signs a fresh assertion for --email on every request, using CORE_FORGE_IDENTITY_KEY;
  • flushes every write immediately, so gRPC-web server streams (such as ApproveChat) arrive live.

It grants nothing new to whoever runs it, because holding the key already lets them sign any address by hand. But anyone who can reach it is signed in as --email. So it defaults to loopback, and on any other address it logs:

WARNING: forge dev proxy listens on <addr>, not loopback: anyone who can reach it is signed in to FORGE as <email>

--connections <dir>: stand in for CORE’s connections

The lane connection picker reads CORE’s connections registry, by name only, from CORE’s own gRPC-web at the page origin. With --connections <dir>, the proxy serves runink.ui.datasources.v1.ConnectionsService read-only over a local registry in <dir>, sealed with CORE_ENVELOPE_KEK. Every write is refused. Seed the registry by hand as <dir>/connections.json, with non-secret fields only:

{"version":1,"connections":[{"id":"erp-orders","name":"ERP orders","type":"postgres"}]}

Without the flag, a call to any of CORE’s own services answers UNIMPLEMENTED, and the picker says the list could not be read.

FORGE_GITHUB_TOKEN

When this is set, FORGE uses the given personal GitHub token for every call instead of minting a platform App installation token. It also stops reading CORE_GH_APP_*, and logs DEV: acting as a personal GitHub token, not the platform App. Everything FORGE then does is that person’s action, with that person’s permissions, including creating real repositories and issues in the organisation.

Run it

Generate throwaway keys

export CORE_FORGE_IDENTITY_KEY=$(head -c32 /dev/urandom | base64) \
       CORE_ENVELOPE_KEK=$(head -c32 /dev/urandom | base64) \
       FORGE_GITHUB_TOKEN=$(gh auth token)

The server and the proxy must share the same CORE_FORGE_IDENTITY_KEY.

Build the bundle with the /forge/ base href

cd flutter && flutter build web --base-href /forge/ && cd ..

Start the server

cd grpc && FORGE_WEB_DIR=../flutter/build/web go run . serve --port 8080 &

By hand, the server listens on 127.0.0.1.

Start the stand-in for CORE

go run . dev proxy --email you@example.org

Open the studio

Open http://127.0.0.1:8081/forge/ in a browser.

To try the planner as well, set INFERENCE_URL and INFERENCE_MODEL to an OpenAI-compatible endpoint you run yourself. Without them, free-form text answers the planner is not connected to the platform's own model — INFERENCE_URL / INFERENCE_MODEL unset on this deployment. Typed commands still work.

What to expect

  • The local appfs root is ~/.local/state/forge/appfs unless FORGE_APPFS_DIR is set. It is sealed under CORE_ENVELOPE_KEK, so keep the same key between runs that share a root.
  • An approval creates a real private repository in FORGE_GITHUB_ORG (default org-runink) and files real issues. To keep a test out of that organisation, point FORGE_GITHUB_ORG at a GitHub organisation you control.
  • Verification stages show “No verification yet” unless CORE’s sweep reaches the repo.

Sources

grpc/cmd/dev.go; grpc/cmd/dev_connections.go; grpc/internal/ghforge/ghforge.go (FromEnv); CLAUDE.md (“Local validation (dev-only affordances)”).