# HAP-1/1 wire profile

Status: experimental reference profile, release 0.1.0. Changes to the signed grammar require a new profile. Existing site AO-1 and profile 0.2 objects are not implicitly accepted.

## Encoding and cryptography

Transport values are canonical JSON, optionally followed by one LF. `parseWire()` rejects alternate encodings, duplicate keys, whitespace variations, and noncanonical numbers. Object member names are ASCII strings sorted lexicographically; strings use JSON escaping and contain printable ASCII only. Numbers are safe integers with magnitude at most 2^53-1; negative zero is prohibited. Reject unknown members, accessors, holes, symbols, and non-plain objects. Maximum depth is 16; arrays have at most 4096 elements; objects at most 256 members; strings at most 16384 characters. Decoders bound messages to 16 MiB before parsing. Payloads containing private/non-ASCII material should be represented by externally computed digests and opaque ASCII references, not their raw contents.

`SHA(value)` is lowercase hex SHA-256 of UTF-8 canonical JSON of `value`. Ed25519 public keys use lowercase hex of the raw 32 bytes; signatures use lowercase hex of 64 bytes. A signature signs UTF-8 bytes of `domain + "\n" + canonical(body)`. All domains below are literal, case-sensitive strings. Signed bodies exclude their `signature` field, except that a ticket excludes only `proof.signature`.

`keyId(publicKey) = SHA({"schema":"HAP-1/key","publicKey":publicKey})`. Fingerprints are identifiers, not proof of human identity. All three keys are paired/provisioned independently; an issuer controls the local owner interface. Never derive authority from a wallet balance or token signature.

| Record | Exact members | Signature domain |
| --- | --- | --- |
| Ticket | `human, delegate, capability, scope, constraints, expiry, reversibility, proof` | `HAP-1/ticket` |
| Challenge | `schema, id, ticket, ticketHash, executor, action, checkpoint, issuedAt, expiresAt, signature` | `HAP-1/challenge` |
| Request | `schema, delegate, challenge, signature` | `HAP-1/request` |
| Receipt | `schema, kind, issuer, sequence, previous, at, ticket, ticketHash, detail, signature` | `HAP-1/receipt` |
| Checkpoint | `schema, issuer, sequence, head, signature` | `HAP-1/checkpoint` |
| Bundle | `schema, from, receipts` | individually signed receipts |
| Bitcoin evidence | `schema, checkpoint, bundle` | checkpoint/receipts, plus optional external OTS proof |

Literal schemas are `HAP-1/challenge`, `HAP-1/request`, `HAP-1/receipt`, `HAP-1/checkpoint`, `HAP-1/bundle`, and `HAP-1/BT-1`, respectively. There is no algorithm negotiation in this profile.

## Ticket and action

`human`, `delegate`, and `scope.audience` are 64-character key fingerprints. `scope` has exactly `{audience, resource}`. A resource/capability/unit reference matches `[A-Za-z0-9][A-Za-z0-9:._/-]{0,127}` and is not case-insensitively `all`, `any`, or `everything`. Equality is exact and case-sensitive.

`constraints` has exactly `{maxAmount, unit, maxUses, freshnessMs}`. Amounts are nonnegative integer minor units. `maxUses` is 1–128 and `freshnessMs` is 100–5000. `proof` has exactly `{schema:"HAP-1/AO-1", id, issuedAt, signature}`. `id` is 16 cryptographically random bytes encoded as 32 hex characters. Times are nonnegative integer Unix milliseconds. `issuedAt < expiry <= issuedAt + 86400000`. `reversibility` is `irreversible` or `compensatable` and grants no extra capability.

A challenge's `ticket` is the ticket ID; `ticketHash = SHA(ticket)`. Its independently random `id` is also 32 hex characters. `executor` equals `scope.audience`. Its `action` is exactly `{capability, resource, amount, unit, digest}`, where `digest = SHA(effectPayload)`. The adapter must derive amount/unit/resource from that same immutable payload and its operator configuration; it cannot take an unrelated user-supplied amount as authority. `checkpoint` is the executor's durable `{sequence, head}` for the paired issuer. `issuedAt < expiresAt <= min(ticket.expiry, issuedAt + freshnessMs)`.

The request embeds the entire signed challenge and the ticket-bound delegate fingerprint. The issuer verifies the delegate and executor signatures against the corresponding fingerprints, exact registered ticket bytes, scope, limits, deadline, and never-reused challenge ID. It atomically spends the amount and one use before releasing a permit. Issued permits are reservations with no automatic refund, including lost responses and failed downstream dispatches.

## Evidence and state

`receipt.kind` is `grant`, `permit`, or `revoke`. `issuer` is the human-device fingerprint. Sequence starts at 1 and increments exactly. `previous` is SHA of the complete preceding signed receipt; genesis is 64 zeroes. `ticketHash` is SHA of the signed ticket. `detail` is `{}` for grant/revoke, or `{challenge, spent, uses}` for a permit, with the entire signed challenge and cumulative counters. The receipt must be issued within the challenge deadline. An individual receipt authenticates an issuer statement; it is not an execution or settlement proof.

The issuer's signed checkpoint names `{sequence, head}` where `head = SHA(lastSignedReceipt)`; genesis is `{sequence:0, head:zeroes}`. `bundle.from` is a cursor of the same shape. Each receipt must extend the preceding cursor, including any overlapping prefix. A verifier must match its retained cursor exactly before advancing. Missing links, lower terminal sequences, or a different head at the retained sequence are refusals. A previously unobserved fork cannot be detected without independently comparing checkpoints.

The executor creates and durably stores challenges itself. A permit must match a pending challenge exactly, have an unexpired ticket/deadline, match the exact effect payload, and extend a verified issuer history. It records all encountered revocations and atomically removes the pending challenge before dispatch. After storage acknowledges consumption, the executor rechecks the clock against its transaction observation and both signed deadlines immediately before entering the effect, with no intervening asynchronous work. A late or invalid clock refuses; the consumed challenge and allowance are never restored. An observed late expiry advances the persisted clock high-water mark. Adapters with asynchronous work inside the effect must enforce the deadline again at their final irreversible boundary. No pending challenge means no act. Receipt verification alone must never call an effect. Normal restarts preserve budgets, kill state, and replay protection; disk rollback/compromise requires external retained checkpoints or stronger storage. The SDK store contract is atomic `read()` and `cas(revision, nextState)`, where `null` means uninitialized. Missing/unavailable storage must not be treated as a permissive default.

Issuer revocation prevents future permit issuance. Received revocation is irreversible for that ticket at the executor. Remote delivery is a separate transport responsibility. A kill arriving after durable dispatch cannot undo an external effect; an undelivered kill leaves at most the already-issued challenge window under honest clocks. Human presence, clock integrity, persistent-storage integrity, physical emergency stopping, global fork discovery, and confirmed Bitcoin inclusion are not inferred from a device signature.

Implementers should run `echo-law-vectors.json` and the stateful regression suite, retain independent checkpoints, and document the actual effect boundary of each adapter.

## Optional presence profile

[HAP-1/WA-1](PRESENCE.md) defines a separate opt-in WebAuthn ceremony and owner-side
permit wrapper. It binds the exact ticket hash, executor challenge ID, action digest
and deadline without adding or changing any HAP-1/1 signed member or signature
domain. Selecting that profile requires its durable issuance policy; it cannot be
inferred from a legacy receipt. User verification is stronger evidence of local
interaction, not legal identity or guaranteed biological liveness.

## Optional local lease profile

[PULSE-1](PULSE.md) adds a separate, opt-in execution wrapper and immutable
per-ticket heartbeat policy. It preserves every HAP-1/1 signature domain/member
and the required fresh owner permit. Its five-second, one-use device lease and
optional private local sensor check never imply legal authorization, identity or
biological liveness. Internet loss does not change permission. Radio loss retains
only the already accepted lease until its original deadline; an exact-action HAP
permit must already be available. Explicit stop, restart and reconnect invalidate
the lease. Received revocation still wins; receipt synchronization cannot repair
an expired window. Letter and HORIZON-1 paths are unchanged.
