Skip to main content
Before you start
  • Two different things say no. Either the key failed, or the key was fine and the sector was not granted. The wording, and the record, differ.
  • The surface changes the answer. The REST read mirror on the Console port and the agent port answer the same refusal in opposite shapes.
  • Read the agent’s own status first. Click Agents & keys, click the agent, and look at its line and its lit toggles.

Where the check happens

Every read goes through one check first. It maps the target to its sector and requires a grant before Postern composes any SQL, looks up any connection, or touches anything upstream. A denial costs no database work and leaves no partial read.
  • It fails closed on an unknown target. A target whose leading segment is not one of the six raises the same access denial as an ungranted sector. A typo’d or invented object name can never map onto a real grant.
  • Postern never compares the presented key as itself. It stores a sha256 hash of the key and looks the grant up by that hash. The raw key is not stored, not logged, and not part of any lookup.
  • Every read carries the owning user as well as the sector. Postern puts a user clause into every SELECT it builds. A row that belongs to another user comes back as absent rather than as a refusal.

The two refusals

Both of these come from the REST read mirror, on the Console port. The agent port answers its own refusals differently — see below. The agent port is the other way round. It answers Unauthorized (401) to a key it refuses, and it does write a row — against the tool initialize, with the reason and no agent name. So a bad key is visible in the record from one surface and invisible from the other. What does not get a row carries the split. Postern keeps the two kinds of “no” apart inside as well. A denial carries a sector when Postern refused a real key a real sector. It carries a reasonunknown agent, revoked or expired — when the key itself failed, and then the row holds no sector at all. A reason never travels in the sector column, so a refused key never appears under a sector filter. What one row holds.

The three reasons a key itself fails

Three states refuse a key: unknown key, revoked key, expired key. Postern puts which one it was in the record, rather than telling the caller. On the agent port the refusal names it: A key that is both revoked and lapsed reports revoked, because the revocation check runs first. Agent keys carries what each verb does to a key, and how to get a working one back.

Refused, or quietly filtered

An ungranted sector is not always an error. Some tools refuse it and some simply leave it out, and the difference decides whether the agent knows it is being held back at all. A name whose leading segment is not one of the six is refused the same way. What the grant decides, tool by tool is the same table read the other way round.

You at the Console are not an agent

Anything that reaches Postern from the machine Postern runs on is the operator. It resolves to the seeded user, with every sector and the agent name operator, with no key and no database round-trip. Grants constrain agents, not you on your own machine. Operator routes never read an Authorization header, and no agent route is ever satisfied by where the request arrived from.

What the record holds about a grant

A sector denial appears as an ordinary row: decision: deny, the sector that was refused, and the reason access denied: <sector>. A refused key is the other case, and it is above. Postern records nothing you do to the grant itself. Create a key, rotate one, edit its sectors or its deadline, revoke it or remove it, and Postern writes no row. Four of those five end every live session that agent holds: a rotate, an edit to its sectors or its deadline, a revoke and a remove. So an agent can stop mid-work with nothing in the record to say why. A Remove deletes the grant but not the trail, because the record’s agent column is plain text with no foreign key to the grant. What a row holds, and the honest limits of a record the database does not enforce.

Confirm it works

  • Ask the agent for something in a sector you did not grant. It is refused, and a row appears under Recent passage with deny and that sector.
  • Ask it for something in a sector you did grant. It answers, and the row reads allow.
  • Point an agent at Postern with a key you have revoked. The agent port answers Unauthorized (401), and a row appears against initialize with reason revoked and no agent name.

If something went wrong

What you have now

Where the check happens, what each surface answers, and how to tell a failed key from an ungranted sector without guessing. A reason never travels in the sector column, so the two are always separable in the record.

Next

Agent keys

what a key is, the deadline it carries, and the four verbs that change or end one

The audit log

what one row holds, what gets a row, and the honest limits of a record the database does not enforce