# pki.sgit.ai — public key infrastructure for agents

> Good public key repositories existed, and were destroyed. In June 2019 the global
> keyserver network was flooded with bogus signatures until importing a poisoned
> certificate would break a working installation; one key reached roughly 150,000
> signatures, and the network's own maintainer called it unsalvageable. The cause was a
> design goal stated at the outset — a key server could add information to a certificate
> but never delete anything — not a bug. This site publishes that history, and the
> registry rules it produces, before the registry exists.

Site version: v0.1.21 (20 August 2026). Published by the sgit project — participant
disclosure at /about/participant.html. All content CC BY 4.0.

## Properties agents may rely on

- Every brief and pack source on this site is fetchable at a stable constructed URL:
  /briefs/<filename>.md and /packs/<pack>/src/<filename>.md. This is a promise, not an
  accident (per the v0.33.61 site access report: agents already rely on constructed
  paths, so the convention is stated rather than left to be inferred).
- Which third of the answer this site holds: nhi.sgit.ai states the problem (no identity
  for rented agents); THIS SITE holds the design; https://sgit.ai/docs/pki.md documents
  the shipped commands. Page-level joins: https://sgit.ai/docs/vault-messaging.md and
  https://sgit.ai/docs/limitations.md carry the choreography and the shipped-versus-
  proposed boundary in the platform's own words.

## Status, stated plainly

The registry does not exist. Everything on this site is stated design, published in
advance so it can be checked against whatever ships. Build-order steps 2 and 3 (the
failure page, the four rules) are done; step 4 (a private registry) is the next build.

## The history

- [Why good public key repositories don't exist](https://pki.sgit.ai/failure/index.html):
  the June 2019 certificate-flooding attack as a dated timeline; the three abused
  properties (unlimited signatures per certificate, anyone may append to anybody's
  certificate, nothing distinguishes a legitimate signature from garbage) mapped onto
  the rules each produces; the never-delete design goal that made repair impossible; and
  what the replacement keyserver gave up (all third-party signatures, and the web of
  trust with them). Externally verifiable and cited.
- The append-only resolution:
  https://pki.sgit.ai/failure/index.html#append-only — append-only is safe when a writer
  appends only to objects it owns, and fatal when anyone may append to somebody else's.
  The rule to carry forward is not "append-only", it is that the writer owns what it
  writes.

## The design, published before the implementation

- [The four registry rules](https://pki.sgit.ai/rules/index.html), each turning around a
  property the attack abused:
  1. Only the owner writes to their own record.
  2. Revocation is a signed append, not a deletion — signed by the key being revoked, so
     the record stays append-only and still supports withdrawal.
  3. Records are size-bounded.
  4. Every entry is signed by something you can check.
- The attestation trade (https://pki.sgit.ai/rules/index.html#attestation): third-party
  attestations are what made the old system valuable and what made it attackable. Permit
  them and rules 1 and 3 must be enforced hard; forbid them and the registry carries no
  social trust signal at all. This is the site's central open question, published
  unresolved.
- What vaults supply (https://pki.sgit.ai/rules/index.html#vaults): distribution, custody
  without access, versioning. What they do not supply: the ownership rule, the size
  bound, signature checking — the registry logic, and the part that failed last time.

## Three questions, three layers

- WHO IS THIS AGENT? — identity, a registry problem. Answered here.
- WHAT MAY IT DO? — mandate, a delegation problem. Answered here.
- SHOULD THAT PRODUCE THIS EFFECT, NOW, HERE? — execution, a broker problem. Named here,
  not owned here: https://pki.sgit.ai/execution/index.html
- The third corner is the receipt. Recording who a key belongs to and what it was permitted
  to do leaves out the most auditable event of all: what it actually did.

## The registry is the missing half of a feature that already ships

- [What already ships](https://pki.sgit.ai/shipped/index.html): `sgit pki keygen` makes TWO
  pairs — RSA-OAEP 4096 for encryption and ECDSA P-256 for signing (NOT X25519/Ed25519).
  Export emits a JSON bundle of PEM blocks; encrypt --recipient <fingerprint>; envelope v2
  wraps an AES-256-GCM content key with RSA-OAEP. The shipped documentation states plainly:
  **no revocation, no directory**. Those two absences are exactly what a registry supplies,
  which makes this a bounded addition to an existing feature rather than a new product.
- The capability model to reuse rather than reinvent: four tiers — append token (write only),
  enumeration key (list/fetch/mark), write key (configure/purge), private key (decrypts, never
  sent). The server stores only hashes of the first three, so a total compromise yields hashes
  rather than capabilities.
- SHIPPED vs PROPOSED: the append lane, its gates, storage and limits are code-verified. The
  client sealing layer and the lane-address derivation `append_token = H(recipient public key)`
  are PROPOSED — no shipped command emits it, and today the token is agreed out of band.
  **Do not code against the derivation.**

## Why agent key registries do not exist: the bootstrap trap

- [The bootstrap trap](https://pki.sgit.ai/bootstrap/index.html): generating a keypair is
  trivial; getting it recognised requires reaching a trusted authority, every route to which
  requires authentication, which requires the identity the agent lacks. That is a loop, not a
  missing feature. Seven common workarounds (operator credential, repo write access, shared bot
  token, vendor integration, cloud credential, project signing secret, bespoke enrolment server)
  each solve transport by creating a larger identity problem. Underneath: ambient authority and
  the confused deputy.
- The reframing: the hardest part of agent identity is not cryptography but *authority
  choreography* — the order in which claims are established. A system can use excellent
  cryptography and still have a weak bootstrap if the first instruction is to hand over a
  platform token.
- The gradient: I control this private key (free, proves possession only) → the project
  recognises this key (a policy decision, not a computation) → the project delegates this
  mandate (scoped, dated, revocable).
- [Enrolment](https://pki.sgit.ai/enrolment/index.html): the exit is a door narrow enough that
  walking through it requires nothing. Posting to an append lane needs a token in the body, no
  account and no access token, and returns a blind acknowledgement. So the path is buildable
  rather than theoretical.

## Two principles, stated exactly

- Encryption restricts who can read a mandate. The signature and subject binding establish who
  may exercise it.
- A signature over an enrolment request proves the submitter controls the corresponding private
  key. It does not prove that the project should trust the agent. Trust is a policy decision
  made afterwards.

## Agent identity

- [Identity and mandate are separate statements](https://pki.sgit.ai/mandate/index.html):
  identity says this key belongs to this agent; a mandate says this agent may do these
  things, until this date, on whose authority. Both signed, both checkable by a third
  party, and the mandate revocable independently of the identity — materially different
  from a bearer token, whose scope is knowable only to its issuer.
- The caution that travels with it: a signed mandate constrains what an agent may be
  authorised to do, not what it does within that authority. It is one control among
  several and does not replace observation or ceilings.
- The limit underneath: a registry can say this key claims to be this agent. It cannot
  say this key is in the hands of that agent and nobody else — that is attestation, and
  neither keys nor vaults supply it.

## Build order and open questions

- [Build order](https://pki.sgit.ai/roadmap/index.html): the collection, the failure
  page, the four rules, a private registry, mandate statements, and a public registry
  last. A registry with one organisation's agents in it is testable; a global one is a
  commitment.
- Six open questions published unresolved
  (https://pki.sgit.ai/roadmap/index.html#open), starting with whether the registry
  accepts third-party attestations at all.
- Honest tensions (https://pki.sgit.ai/roadmap/index.html#tensions), including that the
  size bound will one day reject a legitimate record.

## The registry MVP pack (dev pack: draft-1 + change control)

- [Leading brief](https://pki.sgit.ai/packs/registry-mvp/index.html): a buildable MVP —
  a public vault holding keys, identities, mandates and grants, open data by principle
  (a registry contains no secrets), single operator, own-agents enrolment. Public in data,
  private in authority: build-order step 4 with the covers off.
- [Architecture](https://pki.sgit.ai/packs/registry-mvp/architecture.html): records are append-only
  hash-chained statement logs keyed by signing fingerprint; the processor is the sole
  write-key holder; the four rules run as processor checks plus a public validator —
  enforcement is verification anybody can re-run.
- [Schemas](https://pki.sgit.ai/packs/registry-mvp/schemas.html): identity, mandate, grant,
  acceptance, revocation. A mandate lives in the ISSUER's record (rule 1, no exceptions);
  the subject appends an acceptance. The registry never contains a live capability — grants
  record hashes of what was issued.
- [Workflows](https://pki.sgit.ai/packs/registry-mvp/workflows.html): the first client is
  a documented page a fresh LLM session follows — verify, enrol (the bootstrap gradient as
  commands), operate-under-mandate. The three-session demo (issuer, subject, verifier
  sharing only public URLs) is the acceptance test that matters.
- [Build order](https://pki.sgit.ai/packs/registry-mvp/build-order.html): read path before
  write path; five phases; every definition of done is a fresh-session test.
- New at v0.1.5: [Diagrams](https://pki.sgit.ai/packs/registry-mvp/diagrams.html) (the
  design as eight checkable pictures), [Change control](https://pki.sgit.ai/packs/registry-mvp/change-control.html)
  (what three v0.33.61 briefs correct in draft-1: grant redefined as what a credential
  permits, with excess authority = grant − mandate as the countable product; the 5 June
  design precedence; the fixture class with a required private_key_published flag read
  before any signature), and the [Tabletop exercise](https://pki.sgit.ai/packs/registry-mvp/tabletop.html)
  (six injects giving the four published rules their first population).
- New at v0.1.10: [UX mockups](https://pki.sgit.ai/packs/registry-mvp/ux-mockups.html)
  (the register interface as intended output, screen by screen: a badge on every edge
  carrying who can verify it, by what method, at what cost, when it was last checked and
  what the answer was; five result states, because denied, unreachable and never-checked
  are three different situations; "verifiable by nobody" rendered as a value rather than
  a blank; a policy page printing "0 rows" and "detects nothing" together), and
  [Wardley maps](https://pki.sgit.ai/packs/registry-mvp/wardley-maps.html) (six maps in
  mermaid's wardley-beta: all the novelty sits in four schema objects; a rented agent's
  evidence chain terminates in a component that is commodity and worthless; the two
  absences are on top of the shipped surface, not underneath it; a policy verdict cannot
  be more solid than the badge two layers below it).
- New at v0.1.12: [User stories, features and workflows](https://pki.sgit.ai/packs/registry-mvp/user-stories.html)
  — the pack as deliverables. Six users (verifier, agent, issuer, processor, policy owner,
  and auditor as its own seat, because only an auditor reads the log backwards);
  twenty-four stories, each with a test that can fail and a tag naming its phase, document
  and screen; fourteen features whose status column reads "everything at phase 0–1 is
  designed and nothing is built"; six workflows; the mandate lifecycle as states; a
  traceability table; and a flat list of what is NOT delivered (enforcement, receipts,
  confidentiality, attestation, a graph browser, a trust score, estimates). Two findings
  came out of writing it, recorded as change-control C12: the policy workflow has no
  acceptance test in the build order, and "log every processor decision" conflicts with
  "the acknowledgement tells the agent nothing" for declined submissions.
- New at v0.1.14: [Observability](https://pki.sgit.ai/packs/registry-mvp/observability.html) — the
  half that makes the declared-mandate layer defensible, and the answer to a question this site
  raised in four places and answered in none: a mandate says what an agent may be AUTHORISED to
  do, not what it does, so who is using it? The answer refuses the question. What is capturable
  is VERIFICATION, NOT USE, and the two come apart both ways — a party that uses a mandate
  without verifying generates nothing, and that party is the weakest relying process there is.
  So the product is the MISSING edges: which parties hold a mandate I issued and have never once
  checked it. The issuer holds both halves, so the join is computable. Check events are written
  by the checker into the ISSUER'S OWN LANE, never a central log — rule 1 applied to telemetry,
  which resolves the notary brief's warning rather than contradicting it, at the deliberate cost
  of foreclosing the aggregate. And because nothing is pushed, the interval between a party's
  checks IS its effective revocation latency, measurable before anything is ever revoked.
- New at v0.1.15: [The grant tree and control labels](https://pki.sgit.ai/packs/registry-mvp/grant-tree.html)
  and [Keys and signatures](https://pki.sgit.ai/packs/registry-mvp/keys-and-signatures.html).
  A grant is a TREE of subgrants, so blast radius is a path through it rather than an item in a
  list — and the load-bearing part is the label on each node, above all who enforces the thing
  standing in the way. The general test needs no vendor claim: A CONTROL BOUNDS A GRANT ONLY WHEN
  IT IS ENFORCED BY SOMETHING THE GRANT DOES NOT INCLUDE, giving boundary / setting / expectation,
  and placing most of what people rely on in the middle tier that reads like a boundary and behaves
  like a setting. Plus the SHORTFALL (mandate minus grant, which hurts operations and is harder to
  detect than excess authority), prohibitions as a dated generated view over a stored allow-list,
  and the correction that COUNTING ACCEPTANCES IS THE ONE METRIC THAT INVERTS — it is maximised by
  making risks easy to accept, so declines and unstatable risks are instrumented first.
  On keys: A SECRET IS DEFINED BY EXPECTATION, NOT BY CONTENT, and the intention has to be recorded
  at issue because a deliberate publication and a leak are indistinguishable afterwards. A
  SIGNATURE'S VALUE COMES ENTIRELY FROM THE SCARCITY OF THE PRIVATE HALF, so per-object keypairs
  with published privates are declined: they leave a hash wearing a signature's clothes, defeat
  their own stated use, and would make the fixture flag true on every row — and a flag that is
  always true is a column, not evidence. The rule adopted instead: A KEY BELONGS TO WHATEVER CAN
  KEEP A SECRET, AND EVERYTHING ELSE IS SIGNED BY SOMETHING THAT CAN.
- BUILT, not specified — and rebuilt at v0.1.19 after its first walkthrough by a stranger:
  [Map your own case](https://pki.sgit.ai/assess/index.html) — a workflow where a visitor picks
  the agents they run and the surface they run on, ticks what they meant the agent to do, and
  sees what is reachable that they never intended, with a decision per gap carrying an acceptor
  and an interval. Specified in [document 14](https://pki.sgit.ai/packs/registry-mvp/user-assessment.html).
  You pick NAMED PRODUCTS (Claude Code, ChatGPT, Le Chat...) inside four surface archetypes; facts
  about your own machine PRUNE the graph; escalation is drawn as an EDGE, so you see the path that
  goes around a stated control rather than reading that one exists; the gap is a picture; and
  controls are things you tick as ALREADY TRUE, with their effect on your own gap computed rather
  than asserted. Risk acceptance is deliberately NOT here — it belongs to the risk product, and the
  v1 that had it was wrong. The library of trees is browsable at
  https://pki.sgit.ai/assess/library.html — every tree drawn, with its raw JSON beside it and the
  selected node highlighted in both. The graph is hand-written SVG with no charting library, because
  a CDN dependency would put a third-party request on a page whose whole argument is that you can
  open the network panel and watch nothing leave.
  Two rules shaped it. STORE THE CHOICES, NOT THE ANSWERS: a completed assessment describes which
  agents somebody runs, holding which credentials, with which containment — assembled, that is a
  serviceable plan for attacking them. So what is stored is identifiers from a public library plus
  fixed options and derived dates, implemented as strictly as it can be: THERE IS NO FREE-TEXT
  INPUT ANYWHERE ON THE PAGE. That also means the acceptor is a ROLE rather than a name, so the
  page cannot meet the pack's own named-acceptor standard — recorded rather than hidden. And
  A STRONG THREAT WITH A WEAK ANSWER PRODUCES DENIAL: the standing meta-analysis on fear appeals
  finds defensive response and behaviour change correlate negatively, so a frightening page with
  no credible action performs worse than saying nothing. Every case therefore ends on something
  the visitor can perform, with the number of their own gaps it closes computed rather than
  asserted. The hosted case has ZERO EFFICACY BY CONSTRUCTION — the containment is the vendor's,
  uninspectable and unattestable — so it ends on a REQUEST rather than a remedy: ask the vendor
  for an endpoint that signs an existing audit record for a named relying party.
  Browser storage here is not a placeholder: it makes the no-collection claim ARCHITECTURAL
  rather than operational, and checkable in the network panel in ten seconds.
  The library of pre-computed trees is public at https://pki.sgit.ai/assess/library.json — classes
  of installation rather than named products, because naming one means measuring it.
- Downloadable: https://pki.sgit.ai/packs/registry-mvp/registry-mvp-briefing-pack.zip — the fifteen
  pack sources, every supporting brief, this file, and the reference implementation, with a briefing
  for a fresh session picking it up cold. It asks for a readiness report rather than an
  implementation: read the supporting material, then the pack, then say whether you have what you
  need or list the questions that block you.
- Document 15 is the first written by SOMEBODY WHO WAS NOT IN THE PACK: an outside session took the
  briefing pack cold and answered "if this were implemented as specified, what would it look like?",
  returning twelve screens as real markup (https://pki.sgit.ai/packs/registry-mvp/mockups.html) and a
  debrief. Verified before adoption: all seven of document 08's load-bearing strings appear VERBATIM,
  its C8 citation is accurate, and it loads with no framework and no third-party request. Five of its
  six findings are things the fixed-width ASCII form could not surface — COLOUR RE-COLLAPSES THE FIVE
  RESULT STATES, the badge's wrap point can produce the exact misreading the badge exists to prevent,
  and a column of five ticks is a page-level tick that document 08 forbids. Adopted as C27-C31.
- Four appendixes. A: a Working Backwards PR/FAQ — the pack from a customer inward, whose
  customer-quote slot is published EMPTY because there is no customer and inventing one is what
  this site's participant rules forbid. B: REP-0001, the normative spec in Python PEP form, with
  RFC 2119 keywords and the sections PEP 1 makes mandatory (Security Implications, How to Teach
  This, Rejected Ideas, Open Issues) — the one place the schemas are current rather than
  superseded-with-a-note, Status Draft, Sponsor field empty because it has no champion. C: change
  C: https://pki.sgit.ai/packs/registry-mvp/doctrine.html — Wardley's FORTY DOCTRINES in six
  categories and four phases, explained for readers who know the maps and not the doctrine, with
  this project rated against every one. Of the 35 that can be rated at this size: 17 practised,
  12 partly, 6 not; five need an organisation and are marked "no basis yet" rather than scored.
  THE SHAPE IS THE FINDING — strong exactly where a documentation-heavy solo effort can be strong
  alone, weak on every doctrine that needs other people. Which makes "nobody outside the project
  has been asked whether this is a need" and "REP-0001 has no sponsor" the same doctrinal hole in
  two places, and makes asking five operators a PHASE I doctrine fix rather than a nice-to-have.
  Raw data: https://pki.sgit.ai/packs/registry-mvp/doctrine/doctrine.json
  D: change control, which sits last (99__change-control.md), because it never stops
  growing: twenty-three corrections and thirty-six decisions. Read it second if you are building
  from documents 00-04, so you read them with the errata in hand — draft-1's definition of `grant`
  was superseded on the day it shipped.
- Status: a design pack with ONE SHIPPED CONSUMER. The registry itself is unbuilt; the assessment
  at /assess is live and is a consumer of the model rather than a piece of the registry.
  Site-agent authored, awaiting project-lead adoption (N6 on comms).

## Sources

- [The documents](https://pki.sgit.ai/documents/index.html): the scoping brief captured
  verbatim and readable in-page — strategy brief v0.33.59, 16 August 2026. Raw markdown
  is the source of truth:
  https://pki.sgit.ai/briefs/v0.33.59__strategy-brief__pki-sgit-keyserver-failure-append-only-ownership-rule.md
- [Participant disclosure](https://pki.sgit.ai/about/participant.html): published by the
  sgit project, which builds the vault layer this registry would be built on — including
  where that approach loses.

## Site

- [How this site is built](https://pki.sgit.ai/admin/index.html)
- [Comms](https://pki.sgit.ai/admin/comms.html): the public working channel
- [Release history](https://pki.sgit.ai/admin/versions.html)

## Related sites

- https://nhi.sgit.ai — non-human identity, blast radius and agentic security. This site
  is the cryptographic half of that site's identity gap; its PKI section was where this
  material was staged.
- https://sgit.ai — the sgit project itself.
