# Secrets & Egress

Keep secret values write-only and restrict outbound HTTP with named handles and explicit public URL prefixes.

Ferrite separates a secret value from an application’s ability to use it. The
Dashboard and MCP can list secret names, but they never return stored values.
Outbound HTTP crosses one audited Gateway boundary and only to destinations
declared by the app.

## Write a secret safely

Open the app’s **Settings → Secrets** view in the Dashboard, enter the name and
value, and save it over the authenticated browser session. An agent can call
the MCP `set_secret` tool with the app, name, and value, then use
`list_secrets` to confirm only the name is present. Never place a secret in a
shell argument or commit it to `ferrite.json`.

## Declare egress

```json filename="ferrite.json"
{
  "services": [],
  "applications": [{
    "name": "shop",
    "artifact_key": "apps/shop@v1",
    "artifact_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
    "egress": [{
      "handle": "stripe",
      "destinations": ["https://api.stripe.com/v1/checkout/sessions"]
    }]
  }]
}
```

The app obtains this authority with `ctx.egress("stripe")`. Requests outside
the declared URL prefixes fail before network access. Do not allow a whole host
because one endpoint is convenient; add each required prefix deliberately.

## Local development

`ferrite dev` denies real egress by default. Use `ferrite dev --egress allow`
only when you intend to call the destinations already declared by the app.
Provider fixtures remain preferable for deterministic tests.

## Logging

Never log secret values, authorization headers, session cookies, password or
reset material, TOTP seeds, raw SAML assertions, or complete payment payloads.
Log a provider request ID and a redacted reason class instead.

See [Webhooks](/developers/webhooks/) and [AI Gateway](/developers/ai-gateway/).
