# Postern, for agents

Postern is a self-hosted personal context gateway. A human runs it on
hardware they own; you connect to THEIR gate, at their address. There is
no central Postern endpoint and no Postern cloud.

## What you get

One MCP surface over the sectors of a person's life — finance, mail,
calendar, contacts, health, home. Reads are served from a local cache in
milliseconds; actions dispatch to the source where the source allows
them. You correlate across sectors yourself: there are no cross-sector
joins, by design.

## Connecting

- Endpoint: the owner's gate address + `/mcp` (Streamable HTTP).
- Local (same machine): connect directly; the owner mints you a key in
  their Console and grants sectors.
- Remote: the owner exposes their gate through their own tunnel. Hosted
  agents (claude.ai and similar) use the gate's built-in OAuth bridge:
  standard discovery + dynamic client registration at the owner's public
  address.
- Auth: a per-agent bearer key (32 random bytes, base64url), shown to
  the owner once and stored by the gate only as a hash — or an OAuth
  token derived from it through the bridge. You never receive, see, or
  need the owner's provider passwords.
- The config block, verbatim (the owner's Console hands them this;
  local harnesses paste it into their MCP config):

      {
        "mcpServers": {
          "postern": {
            "type": "http",
            "url": "http://<owner-gate-address>/mcp",
            "headers": { "Authorization": "Bearer <your-agent-key>" }
          }
        }
      }

- There is no stdio transport on a running gate — the gateway owns its
  own stdin. Local means the block above with a localhost address.
- A REST mirror serves the reads for tools that do not speak MCP;
  acting on a source goes through MCP.

## The tools

    describe_context   what you are granted + per-connection freshness
    get_schema         one object's fields, filters, and semantics
    query              cache read: filters, fields, time ranges, aggregates
    get_record         one record by id
    fetch_live         the one read-time path to the source, when freshness matters
    list_actions       what the granted sectors let you DO
    invoke_action      act on the source; a targeted re-sync reflects the result

Start with `describe_context`. Read `get_schema`'s notes before
composing filters — sign conventions and unit traps live there. Responses
are capped: `has_more: true` means you saw a page, not an answer.

## The rules of the gate

- Grants are per-sector and default-deny. You see nothing the owner has
  not granted; within a grant you request bounded slices, never a dump.
- A refusal is explicit, and every visit — reads, actions, refusals —
  lands in an append-only ledger the owner reads.
- The owner can revoke your key in one click. Nothing upstream changes;
  you simply stop resolving.
- Mail bodies are never cached; envelope metadata is served and content
  is fetched on demand.

## No gate yet?

Your human brings one up in minutes on a machine they control:

    # pulls the published image, ghcr.io/getpostern/postern
    git clone https://github.com/getpostern/postern.git && cd postern
    echo "POSTGRES_PASSWORD=$(openssl rand -hex 24)" > .env
    docker compose up -d

Then: Console → connect sources → mint you a key → grant sectors.
Per-provider field guides: getpostern.com/guides. Source: AGPL-3.0,
github.com/getpostern/postern.
