Outposts & the BYO worker model
The quickstart walks the commands; this page explains why they're shaped that way. Four ideas carry most of the design.
The Outpost is the sole tenant boundary
An Outpost is the one unit Pnyon authorizes, isolates, and scopes against. Every stored vector, every mission, and every token carries an Outpost id, and the store filters on it — a caller scoped to one Outpost can never read or write another's rows. There is no tenant granularity above or below it.
A repository is not a tenant. One Outpost holds many repositories; a repository is just a metadata label on the rows within an Outpost, not a separate isolation boundary. This is what lets context earned in one repo become queryable memory for an agent working in another — they share an Outpost, so they share a memory space, while everything outside the Outpost stays sealed off.
Bring your own worker, and attest to it
Rather than run your code on shared compute, you run the pnyon agent on a machine you
control. It has to prove itself before Pnyon will route work to it, and today it does so by
self-attestation:
- On first
pnyon worker connect, the CLI generates an ed25519 keypair under~/.pnyon/. The private key stays on your machine (0600, never printed); only the public key leaves, once, inside your registration request. - Your steward vouches for you out-of-band, then trusts your public key and mints a runner token scoped to your Outpost. An untrusted key is rejected outright.
This is honest about what it proves: it proves you control the key your steward trusted — not that you're running an unmodified official build. That's an interim model. When the signed- release pipeline lands, the trust root moves from per-adopter keys to the release signer's key, and per-adopter key generation is retired.
Outposts are addressed in the URL path
Your worker connects with a plain WebSocket, and a standard WebSocket client can't set
request headers on the opening handshake. So the Outpost isn't carried in a header — it lives
in the URL path the client fully controls:
wss://dispatch.pnyon.com/outpost/<your-outpost-id>The same rule holds for the HTTP submit path (/outpost/<id>/embed). One routing rule for
both transports, with the Outpost as a first-class path segment. The Outpost id is a
non-secret routing identifier; the submit token stays the secret and is carried separately.
Fail-closed by default
Every gate in the path denies rather than fakes. A missing or malformed Outpost segment is
rejected. An unknown, expired, or wrong-scope token resolves to nothing and is denied. An
untrusted attestation key is refused. A missing or wrong submit token answers 401 before
any embed work runs. And if any invariant fails while producing an embedding, the system
errors and persists nothing — it never returns a fabricated vector.
The practical consequence: when something isn't wired correctly, you get a clear rejection, not silently wrong data. That's the posture the whole BYO model is built to preserve.