@suveren/gateway 0.19.4 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/content/guides/1-interview.md +6 -0
  2. package/content/guides/2-package.md +4 -1
  3. package/content/guides/3-mandates.md +3 -0
  4. package/content/guides/6-risks.md +2 -0
  5. package/content/integrations/crm.json +1 -1
  6. package/content/integrations/deploy-github.json +1 -1
  7. package/content/integrations/erp.json +1 -1
  8. package/content/integrations/mail.json +1 -1
  9. package/content/integrations/records.json +1 -1
  10. package/dist/control-plane/index.mjs +21 -13
  11. package/dist/mcp-server/{chunk-ZRRZFGRL.mjs → chunk-APIQIEUA.mjs} +39 -27
  12. package/dist/mcp-server/http.mjs +382 -97
  13. package/dist/mcp-server/report-verify-cli.mjs +1 -1
  14. package/dist/ui/assets/index-1gCMlbLl.js +116 -0
  15. package/dist/ui/assets/{index-RCFQUjaF.css → index-blnpvM2g.css} +1 -1
  16. package/dist/ui/index.html +2 -2
  17. package/node_modules/@hap/core/README.md +92 -29
  18. package/node_modules/@hap/core/dist/index.d.mts +614 -593
  19. package/node_modules/@hap/core/dist/index.d.ts +614 -593
  20. package/node_modules/@hap/core/dist/index.js +774 -749
  21. package/node_modules/@hap/core/dist/index.mjs +744 -724
  22. package/node_modules/@hap/core/package.json +1 -1
  23. package/node_modules/@hap/core/src/base64url.ts +39 -0
  24. package/node_modules/@hap/core/src/commitment-modes.ts +0 -21
  25. package/node_modules/@hap/core/src/errors.ts +76 -0
  26. package/node_modules/@hap/core/src/frame.ts +47 -178
  27. package/node_modules/@hap/core/src/gatekeeper.ts +219 -600
  28. package/node_modules/@hap/core/src/identity.ts +9 -12
  29. package/node_modules/@hap/core/src/index.ts +8 -4
  30. package/node_modules/@hap/core/src/mandate.ts +177 -166
  31. package/node_modules/@hap/core/src/owner-signature.ts +282 -0
  32. package/node_modules/@hap/core/src/profile.ts +234 -0
  33. package/node_modules/@hap/core/src/ticket.ts +148 -0
  34. package/node_modules/@hap/core/src/types.ts +191 -344
  35. package/node_modules/@hap/core/src/versions.ts +47 -0
  36. package/package.json +2 -2
  37. package/profiles/calendar/0.6.profile.json +191 -0
  38. package/profiles/charge/0.6.profile.json +211 -0
  39. package/profiles/customers/0.9.profile.json +182 -0
  40. package/profiles/delegation/0.3.profile.json +118 -0
  41. package/profiles/deploy/0.11.profile.json +177 -0
  42. package/profiles/email/0.8.profile.json +226 -0
  43. package/profiles/index.json +12 -1
  44. package/profiles/publish/0.6.profile.json +187 -0
  45. package/profiles/purchase/0.7.profile.json +235 -0
  46. package/profiles/records/0.6.profile.json +118 -0
  47. package/profiles/reporting/0.3.profile.json +98 -0
  48. package/profiles/sales/0.4.profile.json +291 -0
  49. package/dist/ui/assets/index-BseWI4iJ.js +0 -116
  50. package/node_modules/@hap/core/src/attestation.ts +0 -173
  51. package/node_modules/@hap/core/src/receipt.ts +0 -74
@@ -1,132 +1,74 @@
1
1
  /**
2
- * HAP Core Types — Agent Demo
2
+ * HAP Core Types — v0.7 ("mandate" / "ticket" / "scope" / "Mandate Owner")
3
3
  *
4
- * Types for agent-oriented profiles with bounded execution.
4
+ * v0.7 is a vocabulary release (protocol.md → *Migration from v0.6*): the
5
+ * wire renames *attestation* → *mandate*, *context* → *scope*, *execution
6
+ * receipt* → *mandate ticket*, and *Decision Owner* → *Mandate Owner*. There
7
+ * is no backward-compat alias here — v0.6 and earlier artifacts are
8
+ * verification-only history outside this package (CLAUDE.md "Decided by the
9
+ * owner": no verify path for 0.5/0.6, no old→new code map).
5
10
  */
6
11
 
7
- // ─── Attestation Types ───────────────────────────────────────────────────────
12
+ // ─── Mandate Types ────────────────────────────────────────────────────────────
8
13
 
9
- export interface AttestationHeader {
10
- typ: 'HAP-attestation';
14
+ export interface MandateHeader {
15
+ typ: 'HAP-mandate';
11
16
  alg: 'EdDSA';
12
- kid?: string;
13
- }
14
-
15
- export interface ResolvedDomain {
16
- domain: string;
17
- did: string;
18
- }
19
-
20
- export interface AttestationPayload {
21
- attestation_id: string;
22
- version: '0.3' | '0.4' | '0.5' | '0.6';
23
- profile_id: string;
24
- /** v0.3 (deprecated) — hash of the authorization frame */
25
- frame_hash?: string;
26
- /** v0.4 — hash of the bounds parameters */
27
- bounds_hash?: string;
28
- /** v0.4 — hash of the context parameters */
29
- context_hash?: string;
30
- execution_context_hash: string;
31
- /**
32
- * v0.4 (deprecated in v0.5) — domain-scoped owners. Optional so v0.5
33
- * attestations that carry only `resolved_owners` remain valid. During the
34
- * transition the AS emits BOTH `resolved_domains` (internal coverage) and
35
- * `resolved_owners` (the v0.5 signed wire field).
36
- */
37
- resolved_domains?: ResolvedDomain[];
38
- /**
39
- * v0.5 — the Decision Owner DIDs this attestation covers (person-centric
40
- * model; replaces the abstract domain in `resolved_domains`). Carried in the
41
- * signed payload so verifiers bind the action to the human(s) who authorized it.
42
- */
43
- resolved_owners?: string[];
44
- gate_content_hashes: Record<string, string>;
45
- /**
46
- * v0.5 (companion spec `intent-disclosure@0.1`) — present iff the attestation
47
- * carries an encrypted-intent disclosure object. `sha256:`-prefixed hash that
48
- * binds the disclosure's `intent_ciphertext` + `approvers_frozen` into the
49
- * signed payload (see {@link computeIntentDisclosureHash}), so a compromised
50
- * AS cannot swap the ciphertext/wrapped keys or alter the approver set
51
- * without invalidating the attestation signature (companion invariant C2).
52
- */
53
- intent_disclosure_hash?: string;
54
- /**
55
- * v0.6 (Identity Assurance) — optional signed overlay carrying the verified
56
- * real-world identity of the Decision Owner(s), one entry per owner. Present
57
- * only when identity is disclosed; an attestation with no `subjects` renders
58
- * as `low` (pseudonymous, no name). See {@link Subject} and
59
- * {@link deriveIdentityLine}.
60
- */
61
- subjects?: Subject[];
62
- /**
63
- * v0.4 — commitment mode chosen by the decision owner at attestation time.
64
- * - 'automatic': agent may invoke bounded tools without per-action review.
65
- * - 'review': every tool call requires an approved proposal before the
66
- * receipt route will sign a receipt.
67
- *
68
- * Cryptographically bound in the signed payload so a compromised SP cannot
69
- * silently flip 'review' → 'automatic'. Absent on v0.3 attestations.
70
- * `review_above_cap` (v0.5): automatic below the signed caps, proposal above.
71
- */
72
- commitment_mode?: 'automatic' | 'review' | 'review_above_cap';
73
17
  /**
74
- * v0.6 (Owner Mandate Signatures) — the Decision Owner's own signature(s)
75
- * over the mandate projection ({@link MandateProjection}), one entry per
76
- * co-signing owner. Carried INSIDE the AS-signed payload, so the AS attests
77
- * to having received it and cannot strip it without invalidating its own
78
- * signature. Optional and additive: an attestation without it behaves as
79
- * before (the v0.5 posture — the AS asserts, nobody co-signs).
18
+ * When present MUST identify the same key as `issuer` (for a `did:key`
19
+ * issuer, the multibase key fingerprint) — Mandate rule 8. The field a
20
+ * party writes (`kid`) loses to the field a party cannot forge (`issuer`).
80
21
  */
81
- owner_mandates?: OwnerMandate[];
82
- issued_at: number;
83
- expires_at: number;
22
+ kid?: string;
84
23
  }
85
24
 
86
- /** Mandate-assurance axis — what custody signed the mandate. Independent of
87
- * identity assurance ({@link Subject.method}). */
88
- export type MandateBinding = 'raw' | 'webauthn' | 'eudi';
25
+ /** Signature-assurance axis — what custody signed the mandate projection or
26
+ * approval. Independent of identity assurance ({@link Subject.method}). */
27
+ export type OwnerSignatureBinding = 'raw' | 'webauthn' | 'eudi';
89
28
 
90
29
  /** Which surface showed the owner what they signed. A DECLARATION, not a proof
91
- * — no verifier can check it; see protocol.md → Owner Mandate Signatures. */
30
+ * — no verifier can check it; see protocol.md → *Owner Signatures*. */
92
31
  export type SigningSurface = 'gatekeeper_local' | 'as_web' | 'wallet_display';
93
32
 
94
33
  /**
95
- * v0.6 — one owner's signature over the mandate projection.
34
+ * v0.7 `mandate_owners` entry (protocol.md → *Mandate field: mandate_owners*).
35
+ * `did` is required; the remaining fields are present together when the
36
+ * owner co-signed and absent together when they did not (Mandate rule 7).
96
37
  *
97
38
  * There is deliberately NO `public_key` field, and it MUST NOT be added: the
98
39
  * verification key is carried in the DID itself (key-bearing `did:key`), so a
99
40
  * non-key-bearing DID fails STRUCTURALLY instead of validating against a key
100
- * the AS could have substituted. `alg` is kept only as curve-confusion
101
- * hygiene; if it disagrees with the DID's key type, the DID is authoritative.
41
+ * the AS could have substituted.
102
42
  */
103
- export interface OwnerMandate {
104
- /** The signing owner's key-bearing DID. MUST be a member of `resolved_owners`. */
43
+ export interface MandateOwnerEntry {
44
+ /** The Mandate Owner's DID. For a co-signing entry, MUST be key-bearing. */
105
45
  did: string;
106
46
  /** Signature algorithm hint. The DID's multicodec wins on any disagreement. */
107
- alg: 'EdDSA' | 'ES256';
47
+ alg?: 'EdDSA' | 'ES256';
108
48
  /** base64url (no padding) signature over the JCS bytes of the mandate projection. */
109
- signature: string;
49
+ signature?: string;
110
50
  /** When the owner signed (unix seconds). */
111
- signed_at: number;
51
+ signed_at?: number;
112
52
  /** Defence-in-depth against duplicate issuance by an HONEST AS only —
113
53
  * AS-side nonce enforcement is no defence against the AS itself. */
114
- nonce: string;
115
- binding: MandateBinding;
54
+ nonce?: string;
55
+ /** Required-present, value-open when a signature is carried: an AS MUST
56
+ * NOT reject an entry for a `binding` value it does not recognize. */
57
+ binding?: OwnerSignatureBinding;
116
58
  signing_surface?: SigningSurface;
117
59
  }
118
60
 
119
61
  /**
120
- * v0.6 Identity Assurance — a signed overlay binding a Decision Owner's verified
121
- * real-world identity to the attestation, gated by HOW the identity was verified.
62
+ * v0.6 Identity Assurance — a signed overlay binding a Mandate Owner's verified
63
+ * real-world identity to the mandate, gated by HOW the identity was verified.
122
64
  *
123
65
  * Two display levels (`assurance`): `low` discloses no name; `high` MAY disclose a
124
66
  * name. At `high`, two trust roots: `as` (the AS operator vouches — valid only
125
67
  * within its own domain) and `external` (an external eID such as EUDI — carries the
126
- * owner's own signature, AS-independent). See review.md → "Identity Assurance".
68
+ * owner's own signature, AS-independent).
127
69
  */
128
70
  export interface Subject {
129
- /** The Decision Owner DID this subject describes (matches an entry in resolved_owners). */
71
+ /** The Mandate Owner DID this subject describes (matches the `mandate_owners` entry). */
130
72
  did: string;
131
73
  /** `low` → no name shown; `high` → the name MAY be shown. */
132
74
  assurance: 'low' | 'high';
@@ -140,19 +82,55 @@ export interface Subject {
140
82
  disclose?: { name: string };
141
83
  /** When the underlying verification was performed (unix seconds). */
142
84
  verified_at?: number;
85
+ }
86
+
87
+ /** protocol.md → *Mandate Payload (v0.7)*. */
88
+ export interface MandatePayload {
89
+ mandate_id: string;
90
+ version: '0.7';
91
+ profile_id: string;
92
+ /** Hash of the canonical bounds string. */
93
+ bounds_hash: string;
94
+ /** Hash of the canonical scope string (sha256 of "" when scope is empty). */
95
+ scope_hash: string;
96
+ execution_context_hash: string;
97
+ /** `sha256` over the JCS serialization of the profile the Gatekeeper
98
+ * provisioned for `profile_id` — the content address of the rulebook this
99
+ * mandate was issued under (new in v0.7). */
100
+ profile_hash: string;
101
+ /** The Authority Server's DID — SHOULD be the `did:key` of the signing key
102
+ * itself (new in v0.7). */
103
+ issuer: string;
104
+ /** Exactly one entry in v0.7 (Mandate rule 7). */
105
+ mandate_owners: MandateOwnerEntry[];
106
+ gate_content_hashes: Record<string, string>;
107
+ commitment_mode: 'automatic' | 'review' | 'review_above_cap';
108
+ /** Required, together with `above_cap_approvers`, iff `commitment_mode === "review_above_cap"`. */
109
+ above_cap_caps?: Record<string, number>;
110
+ above_cap_approvers?: string[];
111
+ /**
112
+ * Present iff the mandate carries an encrypted-intent disclosure object
113
+ * (companion spec `intent-disclosure@0.1`) — `sha256:`-prefixed hash
114
+ * binding `intent_ciphertext` + `approvers_frozen` into the signed payload.
115
+ */
116
+ intent_disclosure_hash?: string;
117
+ /** Signed identity-assurance block, one entry per owner — present iff the
118
+ * Mandate Owner disclosed identity. */
119
+ subjects?: Subject[];
143
120
  /**
144
- * @deprecated v0.6 — replaced by {@link AttestationPayload.owner_mandates}.
145
- * This field signed the wrong object (the identity claim, not what was
146
- * committed to) and was welded to one method. Implementations MUST NOT emit
147
- * it; verifiers MAY ignore it on artifacts that carry it. Kept readable for
148
- * audit of pre-0.6 artifacts only.
121
+ * The owner's narrowing of the profile's `disclose_fields` list (new in
122
+ * v0.7) — a subset of the profile's own list. See *Ticket Disclosure Is
123
+ * Declared*.
149
124
  */
150
- owner_signature?: string | null;
125
+ disclose_fields?: string[];
126
+ issued_at: number;
127
+ expires_at: number;
151
128
  }
152
129
 
153
- export interface Attestation {
154
- header: AttestationHeader;
155
- payload: AttestationPayload;
130
+ export interface Mandate {
131
+ header: MandateHeader;
132
+ payload: MandatePayload;
133
+ /** base64url (no padding) Ed25519 signature over the JCS-canonical payload. */
156
134
  signature: string;
157
135
  }
158
136
 
@@ -191,25 +169,13 @@ export interface FieldConstraint {
191
169
  }
192
170
 
193
171
  /**
194
- * Frame field definition within a profile.
195
- */
196
- export interface ProfileFrameField {
197
- type: 'string' | 'number';
198
- required: boolean;
199
- description?: string;
200
- constraint?: FieldConstraint;
201
- enum?: string[];
202
- }
203
-
204
- /**
205
- * Bound enforcement semantics — how a v0.4 bound is checked.
172
+ * Bound enforcement semantics — how a bound is checked.
206
173
  *
207
- * Every bounds field in a v0.4 profile declares a `boundType` so the SP
208
- * receipt route and hap-core gatekeeper can dispatch on it directly,
209
- * without parsing field names or guessing conventions. This is the
210
- * single source of truth for "what does this bound mean at enforcement
211
- * time" — if a new kind is needed, add a variant here and update the
212
- * dispatch sites.
174
+ * Every bounds field in a profile declares a `boundType` so the AS and
175
+ * hap-core gatekeeper can dispatch on it directly, without parsing field
176
+ * names or guessing conventions. This is the single source of truth for
177
+ * "what does this bound mean at enforcement time" — if a new kind is needed,
178
+ * add a variant here and update the dispatch sites.
213
179
  *
214
180
  * See `ProfileBoundsField.boundType`.
215
181
  */
@@ -219,46 +185,45 @@ export type BoundType =
219
185
  * satisfy `execution[of] <= bound` for the current call. No cumulative
220
186
  * tracking. Used by: amount_max, recipient_max, booking_duration_max, etc.
221
187
  *
222
- * `requiredFor` (v0.8+) closes the converse: without it, a call that does
223
- * not carry `of` at all is simply skipped — a 5,000 cap refuses 6,000 but
188
+ * `requiredFor` closes the converse: without it, a call that does not
189
+ * carry `of` at all is simply skipped — a 5,000 cap refuses 6,000 but
224
190
  * permits a call that declares no value whatsoever, which is indistinguishable
225
- * from an unenforced bound. The same hole, and the same fix, as
226
- * `FieldConstraint.requiredFor` on a scope constraint (below): values drawn
227
- * from the profile's `boundsSchema.actionTypes` registry. For a listed
228
- * `action_type`, an execution whose context lacks `of` — or whose value is
229
- * not a finite number — MUST be refused rather than skipped. For an
230
- * unlisted (or when `requiredFor` is absent) action type, today's
231
- * skip-on-absence behaviour is unchanged. Meaningful only on
232
- * `per_transaction`; the other `BoundType` kinds have no per-call "value
233
- * exposed or not" question — `cumulative_sum`/`cumulative_count` always read
234
- * from the execution log regardless of what this call declares, and `enum`
235
- * is a capability flag, not a runtime value — so the field does not exist
236
- * on those variants and a profile that puts it there has it silently
237
- * ignored by every enforcement point (TypeScript also refuses it when the
238
- * profile is authored against this type rather than raw JSON).
191
+ * from an unenforced bound. Values are drawn from the profile's
192
+ * `boundsSchema.actionTypes` registry. For a listed `action_type`, an
193
+ * execution whose context lacks `of` — or whose value is not a finite
194
+ * number — MUST be refused rather than skipped. `appliesTo` MUST NOT be
195
+ * declared on a `per_transaction` bound (protocol.md → Bounds Schema rule 7)
196
+ * — it applies wherever `of` is present in the execution context.
239
197
  */
240
198
  | { kind: 'per_transaction'; of: string; requiredFor?: string[] }
241
199
  /**
242
- * Cumulative sum within a time window. The SP maintains a running sum
243
- * of `execution[of]` across all prior executions in the window; the
200
+ * Cumulative sum within a time window. The AS maintains a running sum
201
+ * of `execution[of]` across all prior tickets in the window; the
244
202
  * current call is approved iff `running_sum + execution[of] <= bound`.
245
- * Used by: amount_daily_max, spend_monthly_max, etc.
203
+ * AS-only enforcement (protocol.md → *Enforcement Authority*); the
204
+ * Gatekeeper MUST NOT enforce this locally. Used by: amount_daily_max,
205
+ * spend_monthly_max, etc.
246
206
  */
247
207
  | { kind: 'cumulative_sum'; of: string; window: CumulativeWindow }
248
208
  /**
249
- * Cumulative count within a time window. Every qualifying execution
209
+ * Cumulative count within a time window. Every qualifying ticket
250
210
  * counts as +1; the current call is approved iff
251
211
  * `running_count + 1 <= bound`. No execution context field is read.
252
- * Used by: write_daily_max, post_monthly_max, booking_daily_max, etc.
212
+ * AS-only enforcement. `appliesTo` is REQUIRED on a profile published
213
+ * under v0.7 or later (Bounds Schema rule 7) — absence would mean
214
+ * "governs every action type", so a forgotten declaration silently
215
+ * throttles every action. Used by: write_daily_max, post_monthly_max,
216
+ * booking_daily_max, etc.
253
217
  */
254
218
  | { kind: 'cumulative_count'; window: CumulativeWindow }
255
219
  /**
256
220
  * String bound restricted to a fixed set of allowed values. The bound's
257
- * value must be one of `values` at attestation time. The gateway
221
+ * value must be one of `values` at mandate time. The gateway
258
222
  * tool-proxy gates tool calls based on the stored bound (via the
259
223
  * integration manifest's `boundField` + `requiredValue`). Not cumulated,
260
- * not checked by the SP receipt route — it's a capability flag.
261
- * Used by: read_access, delete_access, archive_access.
224
+ * not checked by the AS ticket route — it's a capability flag.
225
+ * `appliesTo` MUST NOT be declared here (it is a capability flag, not an
226
+ * action-scoped limit). Used by: read_access, delete_access, archive_access.
262
227
  */
263
228
  | { kind: 'enum'; values: readonly string[] };
264
229
 
@@ -279,11 +244,11 @@ export type FieldUnit =
279
244
  | 'percent';
280
245
 
281
246
  /**
282
- * Bounds field definition within a v0.4 profile.
247
+ * Bounds field definition within a profile.
283
248
  *
284
- * v0.4 adds the required `boundType` — an explicit declaration of how
285
- * the bound is enforced. The older `constraint.enforceable` pattern is
286
- * deprecated and superseded by `boundType`.
249
+ * Every non-metadata bounds field MUST declare `boundType` — an explicit
250
+ * declaration of how the bound is enforced. Implementations MUST fail
251
+ * closed on any bounds field that omits it (Bounds Schema rule 2).
287
252
  */
288
253
  export interface ProfileBoundsField {
289
254
  type: 'string' | 'number';
@@ -291,26 +256,18 @@ export interface ProfileBoundsField {
291
256
  description?: string;
292
257
  displayName?: string;
293
258
  format?: 'email' | 'domain' | 'url' | 'currency';
294
- /**
295
- * v0.4 enforcement semantics. Required for all new profiles.
296
- * Optional here only so the type stays backward compatible with v0.3
297
- * profiles that predate the boundType convention — consumers must
298
- * treat a missing boundType as an error when running in v0.4 mode.
299
- */
259
+ /** Enforcement semantics. REQUIRED on every bounds field except `profile`. */
300
260
  boundType?: BoundType;
301
261
  /**
302
262
  * Which execution action types this bound governs, e.g. `["write"]`.
303
263
  *
304
- * When present, this is authoritative. When absent, enforcement falls back to
305
- * inferring the action type from the FIELD NAME (`send_daily_max` → `send`),
306
- * which is a convention rather than a contract: a profile that names a bound
307
- * after the domain concept instead of the action — calendar's
308
- * `booking_daily_max` against an action type of `write` — matches nothing and
309
- * is skipped, so a limit the user set is never applied and nothing says so.
310
- *
311
- * Declaring it removes the guess. Deliberately additive: profiles migrate one
312
- * at a time, and every remaining fallback is logged, so the cases still
313
- * relying on the naming convention become visible instead of staying silent.
264
+ * When present, this is authoritative. When absent, a `cumulative_sum` bound
265
+ * (or any bound on a profile published before v0.7) governs every action
266
+ * type in the profile's `actionTypes` registry; a `cumulative_count` bound
267
+ * on a v0.7+ profile MUST declare it (Bounds Schema rule 7). MUST NOT be
268
+ * declared on a `per_transaction` bound (it applies wherever its `of`
269
+ * field is present in the execution context) or on an `enum` bound (a
270
+ * capability flag, not an action-scoped limit).
314
271
  */
315
272
  appliesTo?: string[];
316
273
  /**
@@ -333,19 +290,15 @@ export interface ProfileBoundsField {
333
290
  * min(value, maximum). Absent → no ceiling.
334
291
  */
335
292
  maximum?: number;
336
- /** @deprecated v0.4: use boundType instead. */
337
- constraint?: FieldConstraint;
338
- /** @deprecated v0.4: use boundType: { kind: 'enum', values: [...] }. */
339
- enum?: string[];
340
293
  }
341
294
 
342
295
  /**
343
296
  * v0.5 Content Provenance — how a profile's action content is hashed into a
344
- * signed receipt (`contentHash`). The ephemeral-content analog of Output
297
+ * signed ticket (`contentHash`). The ephemeral-content analog of Output
345
298
  * Provenance: it binds the *bytes* of the action rather than a location.
346
299
  *
347
300
  * Profile-bound and OPTIONAL. Absent → no content hash is produced (full
348
- * backward compatibility). The gateway computes the hash; the SP only ever
301
+ * backward compatibility). The gateway computes the hash; the AS only ever
349
302
  * receives the hash, never the content, so HAP's privacy-minimal design holds.
350
303
  *
351
304
  * At `version:"1"` the profile declares only the *policy* — whether to bind and
@@ -409,16 +362,17 @@ export interface ContentBinding {
409
362
  }
410
363
 
411
364
  /**
412
- * Context field definition within a v0.4 profile.
365
+ * Scope field definition within a profile (renamed from `ProfileContextField`
366
+ * in v0.7 — protocol.md → *Migration from v0.6*, `contextSchema` → `scopeSchema`).
413
367
  */
414
- export interface ProfileContextField {
368
+ export interface ProfileScopeField {
415
369
  type: 'string' | 'number';
416
370
  required: boolean;
417
371
  description?: string;
418
372
  displayName?: string;
419
373
  format?: 'email' | 'domain' | 'url' | 'currency';
420
374
  /**
421
- * v0.6 — what this field NAMES on the read path. `counterparty` = the other
375
+ * What this field NAMES on the read path. `counterparty` = the other
422
376
  * party to a communication (matched against an item's participants);
423
377
  * `resource` = the container an item belongs to (a direct attribute match —
424
378
  * and a resource scope enforced on writes MUST also bind reads). Absent →
@@ -427,7 +381,6 @@ export interface ProfileContextField {
427
381
  */
428
382
  scopeKind?: 'counterparty' | 'resource';
429
383
  constraint?: FieldConstraint;
430
- enum?: string[];
431
384
  }
432
385
 
433
386
  /**
@@ -446,13 +399,11 @@ export interface DeclaredFieldDef {
446
399
  export type CumulativeWindow = 'daily' | 'weekly' | 'monthly';
447
400
 
448
401
  /**
449
- * Execution context field definition — cumulative source (resolved from execution log).
402
+ * Execution context field definition — cumulative source (resolved from the
403
+ * AS's ticket history; see protocol.md → *Cumulative State*).
450
404
  *
451
- * The gatekeeper resolves these by querying the execution log:
452
405
  * - `cumulativeField`: which declared field to sum (use "_count" for plain counting)
453
406
  * - `window`: time window for aggregation (daily, weekly, monthly)
454
- *
455
- * The resolved value = running total within window + current call value.
456
407
  */
457
408
  export interface CumulativeFieldDef {
458
409
  source: 'cumulative';
@@ -469,28 +420,8 @@ export interface CumulativeFieldDef {
469
420
  export type ExecutionContextFieldDef = DeclaredFieldDef | CumulativeFieldDef;
470
421
 
471
422
  /**
472
- * Gate question definition.
473
- * @deprecated v0.4 uses a single intent gate with no profile-specific questions.
474
- */
475
- export interface GateQuestion {
476
- question: string;
477
- required: boolean;
478
- }
479
-
480
- /**
481
- * @deprecated Execution paths removed in v0.4. Kept for backward compatibility.
482
- */
483
- export interface ExecutionPath {
484
- description: string;
485
- requiredDomains?: string[];
486
- ttl?: { default: number; max: number };
487
- }
488
-
489
- /**
490
- * Agent Profile — defines constraint types, execution paths, gate questions,
491
- * and the frame/bounds/context schemas for bounded execution.
492
- *
493
- * Supports both v0.3 (frameSchema) and v0.4 (boundsSchema + contextSchema).
423
+ * Agent Profile — defines the bounds/scope/execution-context schemas, gates,
424
+ * TTL policy, and retention for a bounded authority (protocol.md → *Profiles*).
494
425
  */
495
426
  /**
496
427
  * A commitment mode a person can choose when signing a mandate. (`review_above_cap`
@@ -498,6 +429,14 @@ export interface ExecutionPath {
498
429
  */
499
430
  export type SignableCommitmentMode = 'automatic' | 'review';
500
431
 
432
+ /** protocol.md → *Owner Signatures* → "Where the requirement lives" — the
433
+ * profile-floor tier: `minBinding` names the weakest `OwnerSignatureBinding`
434
+ * this profile accepts for a co-signing owner. */
435
+ export interface ProfileOwnerSignatureFloor {
436
+ required: boolean;
437
+ minBinding?: OwnerSignatureBinding;
438
+ }
439
+
501
440
  export interface AgentProfile {
502
441
  id: string;
503
442
  name?: string;
@@ -520,9 +459,10 @@ export interface AgentProfile {
520
459
  whatsNew?: string;
521
460
 
522
461
  /**
523
- * Whether receipts under this profile may be looked up BY THEIR CONTENT — a
524
- * verifier holding the content supplies its hash and learns which receipts
525
- * bind it, without needing a receipt id.
462
+ * Whether tickets under this profile may be looked up BY THEIR CONTENT — a
463
+ * verifier holding the content supplies its hash and learns which tickets
464
+ * bind it, without needing a ticket id. (Renamed from `receipt_lookup` in
465
+ * v0.7.)
526
466
  *
527
467
  * OFF unless declared, and that default is the point. The lookup is a
528
468
  * confirmation oracle: given a guess at the content it says whether that
@@ -534,13 +474,8 @@ export interface AgentProfile {
534
474
  * Enable only when the bound content is unguessable enough that producing it
535
475
  * is equivalent to already having it: prose, an artifact URL, a whole record
536
476
  * payload. Never for a binding over a short value drawn from a small set.
537
- *
538
- * Why it must exist at all: most consequential actions cannot carry their
539
- * receipt id. A released build was built before the receipt existed, a
540
- * content-addressed artifact would change identity if the id were added, and
541
- * a forwarded message has usually lost the footer that carried it.
542
477
  */
543
- receipt_lookup?: boolean;
478
+ ticket_lookup?: boolean;
544
479
 
545
480
  /**
546
481
  * The commitment modes a mandate under this profile may be signed with.
@@ -556,22 +491,14 @@ export interface AgentProfile {
556
491
  commitment_modes?: readonly SignableCommitmentMode[];
557
492
 
558
493
  /**
559
- * v0.3 frame schema (deprecated, kept for backward compat).
560
- * Used when boundsSchema is not present.
561
- */
562
- frameSchema?: {
563
- keyOrder: string[];
564
- fields: Record<string, ProfileFrameField>;
565
- };
566
-
567
- /**
568
- * v0.4 bounds schema — defines the authorization bounds parameters.
494
+ * The bounds schema — the enforceable parameters (protocol.md → *Bounds
495
+ * Schema*).
569
496
  */
570
497
  boundsSchema?: {
571
498
  keyOrder: string[];
572
499
  fields: Record<string, ProfileBoundsField>;
573
500
  /**
574
- * v0.5+ registry of the action types this profile recognizes (e.g.
501
+ * Registry of the action types this profile recognizes (e.g.
575
502
  * `["send", "delete", "setup"]`). Every `execution.action_type` the
576
503
  * Authority Server and gateway accept for this profile MUST be a member
577
504
  * when the registry is declared (protocol.md → Bounds Schema, rule 2);
@@ -583,96 +510,48 @@ export interface AgentProfile {
583
510
  };
584
511
 
585
512
  /**
586
- * v0.4 context schema — defines the execution context parameters (e.g., currency, action_type).
587
- * May be absent or empty for profiles with no static context.
513
+ * The scope schema — operational scoping fields that stay local (renamed
514
+ * from `contextSchema` in v0.7). May be absent or empty for profiles with
515
+ * no static scope.
588
516
  */
589
- contextSchema?: {
517
+ scopeSchema?: {
590
518
  keyOrder: string[];
591
- fields: Record<string, ProfileContextField>;
519
+ fields: Record<string, ProfileScopeField>;
592
520
  };
593
521
 
594
522
  executionContextSchema: {
595
523
  fields: Record<string, ExecutionContextFieldDef>;
596
524
  };
597
525
 
598
- /** @deprecated Execution paths removed in v0.4. Kept for backward compatibility. */
599
- executionPaths?: Record<string, ExecutionPath>;
600
-
601
526
  requiredGates: string[];
602
527
 
603
- /**
604
- * v0.4: no gateQuestions — intent prompt is universal, defined in the gateway UI.
605
- * v0.3: profile-specific gate questions (deprecated).
606
- */
607
- gateQuestions?: {
608
- problem?: GateQuestion;
609
- objective?: GateQuestion;
610
- tradeoffs?: GateQuestion;
611
- };
612
-
613
528
  ttl: { default: number; max: number };
614
529
  retention_minimum: number;
615
530
 
616
531
  /**
617
- * v0.5 Content Provenance (OPTIONAL, profile-bound). When present, the
532
+ * Content Provenance (OPTIONAL, profile-bound). When present, the
618
533
  * gateway computes a `contentHash` for gated writes under this profile and
619
- * passes it (hash only) to the SP, which signs it into the receipt. Absent
534
+ * passes it (hash only) to the AS, which signs it into the ticket. Absent
620
535
  * → no content hash. See {@link ContentBinding}.
621
536
  */
622
537
  content_binding?: ContentBinding;
623
538
 
624
539
  /**
625
- * Tool gating configuration — how MCP tools map to execution context.
626
- * @deprecated Tool gating now lives in integration manifests (content/integrations/*.json).
627
- * Kept for backward compatibility with profiles that still include it.
540
+ * The execution-context fields a ticket under this profile MAY disclose
541
+ * (new in v0.7 — protocol.md → *Ticket Disclosure Is Declared*). Absent →
542
+ * nothing is disclosed by default. A mandate's own `disclose_fields` (when
543
+ * present) MUST be a subset of this list — a mandate may narrow disclosure,
544
+ * never widen it.
628
545
  */
629
- toolGating?: ProfileToolGating;
630
- }
631
-
632
- // ─── Tool Gating Types ───────────────────────────────────────────────────
633
-
634
- /**
635
- * Available transforms for array-aware execution mappings.
636
- * - length: array length → number
637
- * - join: array items joined by comma → string
638
- * - join_domains: extract email domains, deduplicate, sort, join → string
639
- */
640
- export type ExecutionMappingTransform = 'join' | 'join_domains' | 'length';
641
-
642
- /**
643
- * Execution mapping value — how a tool argument maps to execution context field(s).
644
- * - string: direct copy (argName → fieldName)
645
- * - { field, divisor }: numeric division (e.g., cents ÷ 100 → EUR)
646
- * - { field, transform }: array transform (e.g., join_domains)
647
- * - Array form: one argument maps to multiple execution fields
648
- */
649
- export type ExecutionMappingValue =
650
- | string
651
- | { field: string; divisor: number }
652
- | { field: string; transform: ExecutionMappingTransform }
653
- | Array<{ field: string; divisor?: number; transform?: ExecutionMappingTransform }>;
546
+ disclose_fields?: string[];
654
547
 
655
- /**
656
- * Tool gating entry — how a tool's calls map to execution context fields.
657
- * Read-only tools use { category: "read" } — they require authorization
658
- * but skip execution context verification.
659
- */
660
- export interface ProfileToolGatingEntry {
661
- executionMapping: Record<string, ExecutionMappingValue>;
662
- staticExecution?: Record<string, string | number>;
663
- /** Read-only tools: require authorization but no execution context checks */
664
- category?: 'read';
665
- }
666
-
667
- /**
668
- * Profile-level tool gating configuration.
669
- * - default: applied to all tools not listed in overrides
670
- * - overrides: per-tool configs keyed by original MCP tool name
671
- * Use { category: "read" } for read-only tools (null is deprecated)
672
- */
673
- export interface ProfileToolGating {
674
- default: ProfileToolGatingEntry;
675
- overrides?: Record<string, ProfileToolGatingEntry | null>;
548
+ /**
549
+ * Profile-floor requirement for an owner signature on a mandate under this
550
+ * profile (protocol.md → *Owner Signatures* → "Where the requirement
551
+ * lives"). Reserved for domains that cannot mean anything without a
552
+ * signature — used more freely it forks `charge` from `charge-with-cosign`.
553
+ */
554
+ ownerSignature?: ProfileOwnerSignatureFloor;
676
555
  }
677
556
 
678
557
  // ─── Execution Log Types ─────────────────────────────────────────────────────
@@ -719,25 +598,20 @@ export interface ExecutionLogQuery {
719
598
  ): number;
720
599
  }
721
600
 
722
- // ─── Frame Types ─────────────────────────────────────────────────────────────
723
-
724
- /**
725
- * Agent frame parameters — mixed types (strings and numbers).
726
- * Keys and values come from the profile's frameSchema.
727
- */
728
- export type AgentFrameParams = Record<string, string | number>;
601
+ // ─── Bounds / Scope param types ──────────────────────────────────────────────
729
602
 
730
603
  /**
731
604
  * Agent bounds parameters — mixed types (strings and numbers).
732
- * Keys and values come from the profile's boundsSchema (v0.4).
605
+ * Keys and values come from the profile's boundsSchema.
733
606
  */
734
607
  export type AgentBoundsParams = Record<string, string | number>;
735
608
 
736
609
  /**
737
- * Agent context parameters — mixed types (strings and numbers).
738
- * Keys and values come from the profile's contextSchema (v0.4).
610
+ * Agent scope parameters — mixed types (strings and numbers). Keys and
611
+ * values come from the profile's scopeSchema (renamed from
612
+ * `AgentContextParams` in v0.7).
739
613
  */
740
- export type AgentContextParams = Record<string, string | number>;
614
+ export type AgentScopeParams = Record<string, string | number>;
741
615
 
742
616
  // ─── Gatekeeper Types ────────────────────────────────────────────────────────
743
617
 
@@ -745,60 +619,33 @@ export type AgentContextParams = Record<string, string | number>;
745
619
  * Request to the Gatekeeper for bounded execution verification.
746
620
  */
747
621
  export interface GatekeeperRequest {
748
- /** The authorization frame (what was attested to) — v0.3 */
749
- frame: AgentFrameParams;
750
- /** Attestation blobs (base64url) for each domain */
751
- attestations: string[];
752
- /** The agent's execution values for this specific action */
622
+ /** The bounds the mandate(s) committed to. */
623
+ bounds: AgentBoundsParams;
624
+ /** Mandate blobs (base64url) to verify against. */
625
+ mandates: string[];
626
+ /** The agent's execution values for this specific action. */
753
627
  execution: Record<string, string | number>;
754
- /** v0.4: context parameters (currency, action_type, etc.) */
755
- context?: AgentContextParams;
628
+ /** Scope parameters (currency, action_type, allowed recipients, etc.) —
629
+ * enforced locally only; never sent to the Authority Server. */
630
+ scope?: AgentScopeParams;
756
631
  /**
757
- * Authorization path used to scope cumulative lookups in the execution log.
758
- *
759
- * Deliberately OUTSIDE `frame`: the frame is hashed and validated against the
760
- * profile's boundsSchema, so an extra key there is rejected as an unknown
761
- * field and breaks attestation verification outright. Cumulative checks
762
- * previously read the path from the frame, where callers cannot legally put
763
- * it — so it resolved to "" while the log stored real paths, no entry ever
764
- * matched, and every running total read zero. The local gate existed but
765
- * could never fire.
632
+ * Authorization path used to scope cumulative lookups in the execution log
633
+ * (display only — see {@link ExecutionLogQuery}; the Gatekeeper MUST NOT
634
+ * enforce cumulative bounds locally, protocol.md → *Enforcement Authority*).
766
635
  */
767
636
  path?: string;
768
637
  }
769
638
 
770
639
  /**
771
- * Structured error from Gatekeeper verification.
640
+ * Structured error from Gatekeeper verification. Codes are the canonical
641
+ * v0.7 surface ({@link HapErrorCode}, `src/errors.ts`) plus the two
642
+ * Gatekeeper-local-only codes that never reach the wire (`FRAME_MISMATCH`
643
+ * is retired with v0.3; a Gatekeeper reports `BOUNDS_HASH_MISMATCH` /
644
+ * `SCOPE_HASH_MISMATCH` for both its own local mismatch and the AS's wire
645
+ * refusal, per protocol.md → *Error Codes*, "Gatekeeper · local").
772
646
  */
773
647
  export interface GatekeeperError {
774
- code:
775
- | 'BOUND_EXCEEDED'
776
- | 'CUMULATIVE_LIMIT_EXCEEDED'
777
- | 'INVALID_SIGNATURE'
778
- | 'TTL_EXPIRED'
779
- | 'FRAME_MISMATCH'
780
- | 'BOUNDS_MISMATCH'
781
- | 'CONTEXT_MISMATCH'
782
- | 'DOMAIN_NOT_COVERED'
783
- | 'INVALID_PROFILE'
784
- | 'MALFORMED_ATTESTATION'
785
- // v0.5/v0.6 receipt-path codes a Gatekeeper must recognize as definitive
786
- // rejections (protocol.md → Pre-flight Receipt Request):
787
- | 'INVALID_ACTION_TYPE'
788
- | 'ATTESTATION_REVOKED'
789
- | 'APPROVAL_REQUIRED'
790
- | 'IDEMPOTENCY_MISMATCH'
791
- // v0.6 owner-mandate codes (protocol.md → Owner Mandate Signatures):
792
- | 'MANDATE_SIGNATURE_REQUIRED'
793
- | 'MANDATE_SIGNATURE_INVALID'
794
- | 'APPROVAL_SIGNATURE_REQUIRED'
795
- | 'APPROVAL_SIGNATURE_INVALID'
796
- // Canonicalization refusals (protocol.md → Bounds & Scope Canonicalization
797
- // → Value encoding). Raised when a value cannot be canonicalized at all —
798
- // a raw LF/CR, or a profile `constraint.pattern` violation. Distinct from
799
- // BOUNDS_MISMATCH / CONTEXT_MISMATCH, which mean the hashes disagree.
800
- | 'BOUNDS_INVALID_VALUE'
801
- | 'CONTEXT_INVALID_VALUE';
648
+ code: import('./errors').HapErrorCode;
802
649
  field?: string;
803
650
  message: string;
804
651
  bound?: string | number;