Skip to content

Latest commit

 

History

History
139 lines (106 loc) · 8.16 KB

File metadata and controls

139 lines (106 loc) · 8.16 KB

How PEAC works

PEAC is a records layer. You publish terms, you issue signed records when something happens, another party verifies them offline, and the records travel across boundaries as portable proof.

The whole protocol is a four-step loop. Most integrations only touch one or two steps directly.

                               +---------------------------+
                               |    1. Publish             |
                               |    /.well-known/peac.txt  |
                               |    peac-issuer.json       |
                               |    JWKS (jwks.json)       |
                               +---------------------------+
                                            |
                                            v
                               +---------------------------+
                               |    2. Issue               |
                               |    issue()                |
                               |    returns compact JWS    |
                               |    typ: interaction-      |
                               |         record+jwt        |
                               +---------------------------+
                                            |
                                            v
                               +---------------------------+
                               |    3. Verify              |
                               |    verifyLocal()          |
                               |    offline                |
                               |    (or /v1/verify)        |
                               +---------------------------+
                                            |
                                            v
                               +---------------------------+
                               |    4. Export / share      |
                               |    PEAC-Receipt header    |
                               |    MCP _meta carrier      |
                               |    A2A metadata carrier   |
                               |    peac-bundle/0.1        |
                               +---------------------------+

1. Publish

An issuer publishes three machine-readable documents:

  • /.well-known/peac.txt — the policy discovery surface. Human-readable terms, pointer to the issuer config, supported payment rails.
  • /.well-known/peac-issuer.json — the issuer config. Points at the issuer's JWKS URI (for example jwks_uri: https://issuer.example.com/.well-known/jwks.json) and declares supported algorithms.
  • JWKS (jwks.json) — the public keys used to sign receipts. Rotated over time with kid identifiers.

Specs: docs/specs/PEAC-TXT.md, docs/specs/PEAC-ISSUER.md. The well-known URLs follow RFC 8615.

2. Issue

When something happens that another party will need to verify, the issuer creates a signed record:

import { issue } from '@peac/protocol';

const { jws } = await issue({
  iss: 'https://api.example.com',
  kind: 'evidence',
  type: 'org.peacprotocol/api-receipt',
  pillars: ['access'],
  extensions: {
    'org.peacprotocol/access': {
      resource: '/api/v1/resource',
      action: 'read',
      decision: 'allow',
    },
  },
  privateKey,
  kid: 'key-2026-01',
});

The output is a compact JWS string (three base64url parts separated by dots). The JOSE header carries typ: interaction-record+jwt, alg: EdDSA, and kid pointing at the key in the issuer's JWKS. The payload carries the claims: iss, kind, type, pillars, iat, jti, optional extensions, optional policy binding.

Key point: the receipt is a compact JWS. The JOSE typ is interaction-record+jwt. The HTTP body that carries the receipt is application/json (or the PEAC-Receipt HTTP header). Do not confuse the JWS typ with an HTTP media type.

3. Verify

Any party with the issuer's public key can verify the record offline — no call back to the issuer required.

import { verifyLocal } from '@peac/protocol';

const result = await verifyLocal(jws, publicKey, {
  issuer: 'https://api.example.com',
});

if (result.valid) {
  console.log(result.claims.type, result.claims.extensions);
}

Under the hood, verifyLocal() checks:

  1. JOSE header hardening (no embedded keys, no crit, no zip, algorithm is Ed25519).
  2. Signature validity against the provided public key.
  3. Kernel constraints (size caps, pointer depth, canonical form).
  4. Schema validity (Wire claims shape, extension groups, pillar taxonomy).
  5. Policy binding (if peac.policy is present, three-state: verified / failed / unavailable).
  6. Timing checks for issued-at (iat) freshness and clock skew where configured; core record validity does not expire by default.

For the hosted path, POST /v1/verify on the reference verifier returns the same deterministic verification report. See packages/schema/openapi/verify.yaml.

4. Export and share

Records travel in several carriers depending on the surface:

  • HTTP: the PEAC-Receipt response header carries a compact JWS (up to 8 KiB in the header; larger records use PEAC-Receipt-Ref pointing at a fetchable resource).
  • MCP: the tool-call response _meta field carries org.peacprotocol/receipt_jws and org.peacprotocol/receipt_ref (up to 64 KiB embed).
  • A2A: the metadata[extensionURI].carriers[] array carries receipt records across Agent-to-Agent flows; handoff observation records are described in docs/specs/A2A-HANDOFF-RECORDS.md.
  • CLI execution: peac observe command emits an unsigned observation; peac record command issues a signed command-execution record using caller-provided issuer material. See docs/specs/CLI-CARRIER-PROFILE.md.
  • Lifecycle observation: peac emit lifecycle issues signed records for caller-reported lifecycle events. See docs/specs/LIFECYCLE-OBSERVATION-PROFILE.md.
  • Bundles: peac-bundle/0.1 packages multiple receipts, JWKS snapshots, and policy artifacts into a portable audit file. See docs/specs/EVIDENCE-CARRIER-CONTRACT.md.
  • Reports: the reference verifier returns a deterministic verification report; the extended application/peac-report+json shape adds timing, report ID, and failure reasons.

PEAC records observations across these carriers; it is not a shell framework, scheduler, eval platform, approval system, or orchestrator.

Kinds and types

A record's kind is one of two fixed structural values — evidence (records what happened) or challenge (requests proof from a peer). A record's type is an open reverse-DNS or URI identifier for what the record represents (for example org.peacprotocol/payment, org.peacprotocol/mcp-tool-call, org.peacprotocol/api-receipt). Extensions are typed data groups organized by pillar (access, attribution, commerce, consent, compliance, privacy, provenance, safety, identity, purpose).

org.peacprotocol/mcp-tool-call is an example custom type URI used by the MCP recipe. It is not a registered PEAC extension group or registered receipt type. The reference public verifier (@peac/protocol.verifyLocal()) emits a type_unregistered warning for unregistered type values, which downstream policy logic may treat as informational. Operators who want a registered MCP-specific receipt type should propose a dedicated PEAC profile and registry entry before relying on it as a registered type.

Normative detail: docs/specs/WIRE-0.2.md and docs/specs/PROTOCOL-BEHAVIOR.md.

What PEAC does not do in this loop

PEAC records what another system attested. It does not:

  • Decide whether an action is allowed or denied.
  • Orchestrate agents, workflows, or commands.
  • Settle payments or custody funds.
  • Issue identity credentials or manage keys beyond the signing path.
  • Host dashboards or telemetry backends.

See docs/WHERE-IT-FITS.md for the boundary next to adjacent systems and docs/WHAT-PEAC-STANDARDIZES.md for what the protocol defines.