Secrets and egress
This file used to be a proposal. It is now current implementation documentation for project-scoped secrets, egress substitution, and integration credential storage in OS itx.
Current shape#
Project secret material is path-addressed and write-only:
await itx.secrets.get("/secrets/openai").update({
material: "...",
egress: { urls: ["https://api.openai.com"] },
});Secret paths are normalized and must start with /secrets/. The public secret
capability has update, __describe, fetch, and processor, but no method
that returns material. __describe() reports metadata only: whether material is
present, the egress allowlist, and usage audit counters.
The implementation lives in:
apps/os/src/domains/secrets/secret-durable-object.tsapps/os/src/domains/secrets/utils.tsapps/os/src/rpc-targets.tsapps/os/src/domains/projects/project-durable-object.tsapps/os/src/domains/projects/egress.ts
Using a secret in outbound requests#
Outbound requests reference secrets with a header placeholder:
const request = new Request("https://api.openai.com/v1/responses", {
method: "POST",
headers: {
authorization: 'Bearer getSecret("/secrets/openai")',
"content-type": "application/json",
},
body: JSON.stringify(body),
});
const response = await itx.egress.fetch(request);getSecret("/secrets/...") is the current itx placeholder.
Substitution happens only inside the project egress path. Interceptors installed
with itx.egress.intercept(handler) run before substitution and see the
placeholder, never raw material.
The project Durable Object is the egress decision point:
- If a live egress interceptor is installed, it handles the request before secret substitution.
- Otherwise, the project DO scans request headers for
getSecret(path)placeholders. - If no secret is referenced, the request is fetched directly.
- If exactly one secret is referenced, the request is forwarded to that Secret Durable Object.
- The Secret DO checks that material exists and that the request origin matches one of the secret's allowed egress URL origins.
- The Secret DO appends
events.iterate.com/secret/used, substitutes matching header placeholders, and performs the terminalfetch.
Dynamic workers use the same egress door: their bare fetch() is wired through
ProjectEgressEntrypoint, which forwards to the project Durable Object.
Security properties#
- Secret material is encrypted before it is written to the secret stream state.
- The public secret surface cannot read material back.
- Egress allowlists are per secret and checked by URL origin.
- Usage is recorded in the secret's stream as audit data.
- Secret substitution currently scans headers only.
- A request can reference at most one secret path. Requests with multiple distinct secret paths are rejected.
The current authorization boundary is the project itx session. A caller who has the authority to use the project itx can update that project's secret metadata and send egress requests through that project's egress door. There is no per-user secret ACL inside a project today.
Integrations#
Slack and Google use different storage paths because their runtime needs are different.
Slack stores the project's bot token in the itx secret system at:
/secrets/integrations/slack/bot-tokenSlack Web API calls place getSecret("/secrets/integrations/slack/bot-token")
in the authorization header and go through project egress, so token material
stays inside the substitution pipeline. The secret is allowlisted for
https://slack.com.
Google OAuth tokens live as AES-GCM ciphertext payloads on the project
/integrations/google stream. Refreshing a Google access token needs raw
refresh-token material in a form body, which the header-only secret
substitution path does not cover, so Google does not use the Secret Durable
Object path today.
OAuth state for Slack and Google is stateless HMAC-signed data, not D1 state.
Slack team routing is stored in the deployment-wide
/integrations/slack-team-directory stream.
Current limits#
These are not implemented in the current itx surface:
- Secret hierarchy across global, org, project, and user scopes.
- Per-user secret authorization within a project.
- Discovery endpoints that list usable secrets for a destination.
- Human-in-the-loop egress approval.
- Generic non-secret environment variable management through this system.
- Secret substitution in request bodies, URLs, or WebSocket upgrade payloads.
- Multiple secret substitutions in a single request.
Future work should extend the current project egress and stream model rather than revive the old table-oriented proposal.