@orangecheck/agent-core 2.6.0 → 2.7.1
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.
- package/README.md +4 -2
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +52 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -11
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.mts +9 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.js.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
- package/src/federation.test.ts +3 -3
- package/src/federation.ts +3 -8
- package/src/index.ts +4 -0
- package/src/live-time.test.ts +151 -0
- package/src/revocation-authority.test.ts +32 -28
- package/src/revocation-failclosed.test.ts +1 -1
- package/src/test-vectors.test.ts +43 -8
- package/src/types.ts +9 -0
- package/src/verify.ts +108 -7
package/dist/types.d.mts
CHANGED
|
@@ -153,7 +153,7 @@ interface RevocationCanonicalInput {
|
|
|
153
153
|
reason: string;
|
|
154
154
|
signed_at: string;
|
|
155
155
|
}
|
|
156
|
-
type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
|
|
156
|
+
type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_STALE' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
|
|
157
157
|
interface VerifyOk<T> {
|
|
158
158
|
ok: true;
|
|
159
159
|
envelope: T;
|
|
@@ -187,6 +187,14 @@ interface VerifyActionOkExtra {
|
|
|
187
187
|
blockHash: string;
|
|
188
188
|
verified: true;
|
|
189
189
|
};
|
|
190
|
+
asOf: {
|
|
191
|
+
basis: 'now';
|
|
192
|
+
now: string;
|
|
193
|
+
} | {
|
|
194
|
+
basis: 'signed_at';
|
|
195
|
+
signedAt: string;
|
|
196
|
+
anchored: boolean;
|
|
197
|
+
};
|
|
190
198
|
}
|
|
191
199
|
type VerifyActionResult = (VerifyOk<ActionEnvelope> & VerifyActionOkExtra) | VerifyErr;
|
|
192
200
|
|
package/dist/types.d.ts
CHANGED
|
@@ -153,7 +153,7 @@ interface RevocationCanonicalInput {
|
|
|
153
153
|
reason: string;
|
|
154
154
|
signed_at: string;
|
|
155
155
|
}
|
|
156
|
-
type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
|
|
156
|
+
type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_STALE' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
|
|
157
157
|
interface VerifyOk<T> {
|
|
158
158
|
ok: true;
|
|
159
159
|
envelope: T;
|
|
@@ -187,6 +187,14 @@ interface VerifyActionOkExtra {
|
|
|
187
187
|
blockHash: string;
|
|
188
188
|
verified: true;
|
|
189
189
|
};
|
|
190
|
+
asOf: {
|
|
191
|
+
basis: 'now';
|
|
192
|
+
now: string;
|
|
193
|
+
} | {
|
|
194
|
+
basis: 'signed_at';
|
|
195
|
+
signedAt: string;
|
|
196
|
+
anchored: boolean;
|
|
197
|
+
};
|
|
190
198
|
}
|
|
191
199
|
type VerifyActionResult = (VerifyOk<ActionEnvelope> & VerifyActionOkExtra) | VerifyErr;
|
|
192
200
|
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB","file":"types.js","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB","file":"types.js","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_STALE'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n /**\n * The time the verdict holds for. `now`: verifyAction, checked live.\n * `signed_at`: verifyActionAsOfSignedAt, checked as of the time the agent\n * claims; `anchored` is false when only the agent says so.\n */\n asOf:\n | { basis: 'now'; now: string }\n | { basis: 'signed_at'; signedAt: string; anchored: boolean };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
|
package/dist/types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAEO,IAAM,gBAAA,GAAmB","file":"types.mjs","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAEO,IAAM,gBAAA,GAAmB","file":"types.mjs","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_STALE'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n /**\n * The time the verdict holds for. `now`: verifyAction, checked live.\n * `signed_at`: verifyActionAsOfSignedAt, checked as of the time the agent\n * claims; `anchored` is false when only the agent says so.\n */\n asOf:\n | { basis: 'now'; now: string }\n | { basis: 'signed_at'; signedAt: string; anchored: boolean };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangecheck/agent-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "OC Agent canonical messages, envelope formats (delegation/action/revocation), scope grammar, and verification. See https://github.com/orangecheck/oc-agent-protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
package/src/federation.test.ts
CHANGED
|
@@ -245,7 +245,7 @@ d('federation delegation revocation check', () => {
|
|
|
245
245
|
};
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
it('honours
|
|
248
|
+
it('honours the agent’s own revocation whatever holders says', async () => {
|
|
249
249
|
const dv = await load('v19-federation-delegation-3of5-valid.json');
|
|
250
250
|
const del = dv.expected.envelope as FederationDelegationEnvelope;
|
|
251
251
|
const principalOnly = { ...del, revocation: { holders: ['principal' as const], ref: null } };
|
|
@@ -253,12 +253,12 @@ d('federation delegation revocation check', () => {
|
|
|
253
253
|
...del,
|
|
254
254
|
revocation: { holders: ['principal' as const, 'agent' as const], ref: null },
|
|
255
255
|
};
|
|
256
|
-
const
|
|
256
|
+
const revokedToo = await verifyFederationDelegation({
|
|
257
257
|
envelope: principalOnly,
|
|
258
258
|
revocations: [agentRevocation(principalOnly)],
|
|
259
259
|
...base,
|
|
260
260
|
});
|
|
261
|
-
expect(
|
|
261
|
+
expect(revokedToo.ok === false && revokedToo.code).toBe('E_REVOKED');
|
|
262
262
|
const revoked = await verifyFederationDelegation({
|
|
263
263
|
envelope: withAgent,
|
|
264
264
|
revocations: [agentRevocation(withAgent)],
|
package/src/federation.ts
CHANGED
|
@@ -230,7 +230,7 @@ export interface VerifyFederationDelegationInput extends VerifyFederationBase {
|
|
|
230
230
|
envelope: FederationDelegationEnvelope;
|
|
231
231
|
/**
|
|
232
232
|
* Revocations to check (kind-30085 by `#t` with the delegation id, SPEC §9.4): federation revocations,
|
|
233
|
-
* and agent
|
|
233
|
+
* and the agent's own revocations.
|
|
234
234
|
* Required unless `skipRevocationCheck` states that revocation is not checked.
|
|
235
235
|
*/
|
|
236
236
|
revocations?: Array<FederationRevocationEnvelope | RevocationEnvelope>;
|
|
@@ -316,9 +316,8 @@ export async function verifyFederationDelegation(
|
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
/**
|
|
319
|
-
* FEDERATION.md §4: the federation (M-of-N) may always revoke
|
|
320
|
-
*
|
|
321
|
-
* includes "agent".
|
|
319
|
+
* FEDERATION.md §4: the federation (M-of-N) may always revoke, and so may the
|
|
320
|
+
* agent with its own BIP-322 signature, whatever `revocation.holders` says.
|
|
322
321
|
*/
|
|
323
322
|
async function verifyRevocationOf(
|
|
324
323
|
d: FederationDelegationEnvelope,
|
|
@@ -334,10 +333,6 @@ async function verifyRevocationOf(
|
|
|
334
333
|
});
|
|
335
334
|
}
|
|
336
335
|
const v1 = rev as RevocationEnvelope;
|
|
337
|
-
const holders = d.revocation?.holders ?? ['principal'];
|
|
338
|
-
if (!Array.isArray(holders) || !holders.includes('agent')) {
|
|
339
|
-
return fail('E_REVOKER_UNAUTHORIZED', 'only the federation may revoke this delegation');
|
|
340
|
-
}
|
|
341
336
|
if (v1.kind !== 'agent-revocation' || v1.signer?.address !== d.agent.address) {
|
|
342
337
|
return fail('E_REVOKER_UNAUTHORIZED', 'a single-address revocation must be signed by the agent');
|
|
343
338
|
}
|
package/src/index.ts
CHANGED
|
@@ -37,15 +37,19 @@ export type { Scope, ScopeConstraint, ScopeOp, ValidationOptions } from './scope
|
|
|
37
37
|
export {
|
|
38
38
|
verifyDelegation,
|
|
39
39
|
verifyAction,
|
|
40
|
+
verifyActionAsOfSignedAt,
|
|
41
|
+
createReplayGuard,
|
|
40
42
|
verifyRevocation,
|
|
41
43
|
verifySubdelegation,
|
|
42
44
|
AgentError,
|
|
43
45
|
DEFAULT_MAX_CHAIN_DEPTH,
|
|
46
|
+
DEFAULT_MAX_SKEW_MS,
|
|
44
47
|
} from './verify.js';
|
|
45
48
|
export type {
|
|
46
49
|
VerifyBase,
|
|
47
50
|
VerifyDelegationInput,
|
|
48
51
|
VerifyActionInput,
|
|
52
|
+
VerifyActionAsOfSignedAtInput,
|
|
49
53
|
VerifyRevocationInput,
|
|
50
54
|
VerifySubdelegationInput,
|
|
51
55
|
} from './verify.js';
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// SPEC §8.1 step 6 checks the delegation at the time of verification, not at
|
|
2
|
+
// the action's signed_at. The agent picks signed_at, so an action that only
|
|
3
|
+
// had to fit the delegation's window could be backdated past its expiry or past
|
|
4
|
+
// a revocation and still verify. A live verifier checks at `now`; an audit of a
|
|
5
|
+
// past action says outright that it relied on the agent's claimed time.
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { computeActionId, computeRevocationId } from './canonical.js';
|
|
9
|
+
import { createReplayGuard, verifyAction, verifyActionAsOfSignedAt } from './verify.js';
|
|
10
|
+
import type { ActionEnvelope, DelegationEnvelope, RevocationEnvelope } from './types.js';
|
|
11
|
+
|
|
12
|
+
const delegation: DelegationEnvelope = {
|
|
13
|
+
v: 1,
|
|
14
|
+
kind: 'agent-delegation',
|
|
15
|
+
id: '36d79600191db871baa3fc9aa3b5e77750a5c423b1f620ec26cf16bd122e19a7',
|
|
16
|
+
principal: { address: 'bc1qprincipal000000000000000000000000000000', alg: 'bip322' },
|
|
17
|
+
agent: { address: 'bc1qagent0000000000000000000000000000000000', alg: 'bip322' },
|
|
18
|
+
scopes: ['lock:seal(recipient=bc1qalice000000000000000000000000000000000)'],
|
|
19
|
+
bond: null,
|
|
20
|
+
issued_at: '2026-04-22T12:00:00Z',
|
|
21
|
+
expires_at: '2026-04-29T12:00:00Z',
|
|
22
|
+
nonce: '0123456789abcdef0123456789abcdef',
|
|
23
|
+
revocation: { holders: ['principal'], ref: null },
|
|
24
|
+
sig: { alg: 'bip322', pubkey: 'bc1qprincipal000000000000000000000000000000', value: 'AAAA' },
|
|
25
|
+
} as DelegationEnvelope;
|
|
26
|
+
|
|
27
|
+
function action(signed_at: string): ActionEnvelope {
|
|
28
|
+
const canon = {
|
|
29
|
+
address: delegation.agent.address,
|
|
30
|
+
content_hash: 'sha256:' + '00'.repeat(32),
|
|
31
|
+
content_length: 0,
|
|
32
|
+
content_mime: 'text/plain',
|
|
33
|
+
signed_at,
|
|
34
|
+
delegation_id: delegation.id,
|
|
35
|
+
scope_exercised: delegation.scopes![0]!,
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
v: 1,
|
|
39
|
+
kind: 'agent-action',
|
|
40
|
+
id: computeActionId(canon),
|
|
41
|
+
content: { hash: canon.content_hash, length: 0, mime: 'text/plain', ref: null },
|
|
42
|
+
signer: { address: canon.address, alg: 'bip322' },
|
|
43
|
+
signed_at,
|
|
44
|
+
delegation_id: delegation.id,
|
|
45
|
+
scope_exercised: canon.scope_exercised,
|
|
46
|
+
ots: null,
|
|
47
|
+
sig: { alg: 'bip322', pubkey: canon.address, value: 'AAAA' },
|
|
48
|
+
} as ActionEnvelope;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function revocation(signed_at: string): RevocationEnvelope {
|
|
52
|
+
const canon = { address: delegation.principal.address, delegation_id: delegation.id, reason: '', signed_at };
|
|
53
|
+
return {
|
|
54
|
+
v: 1,
|
|
55
|
+
kind: 'agent-revocation',
|
|
56
|
+
id: computeRevocationId(canon),
|
|
57
|
+
delegation_id: delegation.id,
|
|
58
|
+
signer: { address: canon.address, alg: 'bip322' },
|
|
59
|
+
reason: '',
|
|
60
|
+
signed_at,
|
|
61
|
+
ots: null,
|
|
62
|
+
sig: { alg: 'bip322', pubkey: canon.address, value: 'AAAA' },
|
|
63
|
+
} as RevocationEnvelope;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const base = { delegation, skipSignatureVerification: true };
|
|
67
|
+
|
|
68
|
+
describe('live verification checks at now', () => {
|
|
69
|
+
it('refuses an action backdated into the window of a delegation that has expired', async () => {
|
|
70
|
+
const r = await verifyAction({
|
|
71
|
+
...base,
|
|
72
|
+
action: action('2026-04-28T00:00:00Z'),
|
|
73
|
+
revocations: [],
|
|
74
|
+
now: new Date('2026-05-01T00:00:00Z'),
|
|
75
|
+
});
|
|
76
|
+
expect(r.ok === false && r.code).toBe('E_EXPIRED');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('refuses an action backdated to before a revocation that is already in effect', async () => {
|
|
80
|
+
const r = await verifyAction({
|
|
81
|
+
...base,
|
|
82
|
+
action: action('2026-04-24T11:59:00Z'),
|
|
83
|
+
revocations: [revocation('2026-04-24T11:59:30Z')],
|
|
84
|
+
now: new Date('2026-04-24T12:00:00Z'),
|
|
85
|
+
});
|
|
86
|
+
expect(r.ok === false && r.code).toBe('E_REVOKED');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('refuses an action signed further from now than the freshness bound', async () => {
|
|
90
|
+
const now = new Date('2026-04-24T12:00:00Z');
|
|
91
|
+
const old = await verifyAction({ ...base, action: action('2026-04-24T11:50:00Z'), revocations: [], now });
|
|
92
|
+
expect(old.ok === false && old.code).toBe('E_STALE');
|
|
93
|
+
const ahead = await verifyAction({ ...base, action: action('2026-04-24T12:05:00Z'), revocations: [], now });
|
|
94
|
+
expect(ahead.ok === false && ahead.code).toBe('E_STALE');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('accepts a fresh action under a clear feed, and says it was checked at now', async () => {
|
|
98
|
+
const now = new Date('2026-04-24T12:00:00Z');
|
|
99
|
+
const r = await verifyAction({ ...base, action: action('2026-04-24T11:59:30Z'), revocations: [], now });
|
|
100
|
+
expect(r.ok).toBe(true);
|
|
101
|
+
expect(r.ok && r.asOf).toEqual({ basis: 'now', now: '2026-04-24T12:00:00.000Z' });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('takes a wider freshness bound when the caller sets one', async () => {
|
|
105
|
+
const r = await verifyAction({
|
|
106
|
+
...base,
|
|
107
|
+
action: action('2026-04-24T11:50:00Z'),
|
|
108
|
+
revocations: [],
|
|
109
|
+
now: new Date('2026-04-24T12:00:00Z'),
|
|
110
|
+
maxSkewMs: 15 * 60_000,
|
|
111
|
+
});
|
|
112
|
+
expect(r.ok).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('does not let a revocation dated in the future refuse an action today', async () => {
|
|
116
|
+
const r = await verifyAction({
|
|
117
|
+
...base,
|
|
118
|
+
action: action('2026-04-24T11:59:30Z'),
|
|
119
|
+
revocations: [revocation('2026-04-25T00:00:00Z')],
|
|
120
|
+
now: new Date('2026-04-24T12:00:00Z'),
|
|
121
|
+
});
|
|
122
|
+
expect(r.ok).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('an audit checks as of signed_at, and says that time is a claim', () => {
|
|
127
|
+
it('accepts a past action inside its window, with an unanchored signed_at', async () => {
|
|
128
|
+
const r = await verifyActionAsOfSignedAt({ ...base, action: action('2026-04-28T00:00:00Z'), revocations: [] });
|
|
129
|
+
expect(r.ok).toBe(true);
|
|
130
|
+
expect(r.ok && r.asOf).toEqual({ basis: 'signed_at', signedAt: '2026-04-28T00:00:00Z', anchored: false });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('still refuses an action that claims a time after the revocation', async () => {
|
|
134
|
+
const r = await verifyActionAsOfSignedAt({
|
|
135
|
+
...base,
|
|
136
|
+
action: action('2026-04-24T12:00:00Z'),
|
|
137
|
+
revocations: [revocation('2026-04-24T11:59:30Z')],
|
|
138
|
+
});
|
|
139
|
+
expect(r.ok === false && r.code).toBe('E_REVOKED');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('createReplayGuard', () => {
|
|
144
|
+
it('admits an action once, and forgets it when it could no longer pass as fresh', () => {
|
|
145
|
+
const guard = createReplayGuard(120_000);
|
|
146
|
+
const a = action('2026-04-24T12:00:00Z');
|
|
147
|
+
expect(guard.firstUse(a, new Date('2026-04-24T12:00:05Z'))).toBe(true);
|
|
148
|
+
expect(guard.firstUse(a, new Date('2026-04-24T12:01:00Z'))).toBe(false);
|
|
149
|
+
expect(guard.firstUse(a, new Date('2026-04-24T12:02:01Z'))).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { describe, expect, it } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { computeActionId, computeDelegationId, computeRevocationId } from './canonical.js';
|
|
8
|
-
import {
|
|
8
|
+
import { verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from './verify.js';
|
|
9
9
|
import type { ActionEnvelope, ActionOts, DelegationEnvelope, RevocationEnvelope } from './types.js';
|
|
10
10
|
|
|
11
11
|
const PRINCIPAL = 'bc1qprincipal000000000000000000000000000000';
|
|
@@ -91,6 +91,8 @@ function confirmed(height: number): ActionOts {
|
|
|
91
91
|
|
|
92
92
|
const AT = new Date('2026-04-23T12:00:00Z');
|
|
93
93
|
|
|
94
|
+
// Priority between an action and a revocation (SPEC §9.3) is a question about
|
|
95
|
+
// the action's own time, so these run as audits, as of signed_at.
|
|
94
96
|
describe('revocation authority always includes the principal', () => {
|
|
95
97
|
for (const holders of [['agent'], [], ['principal'], undefined, 'agent']) {
|
|
96
98
|
it(`principal revocation is honoured with holders=${JSON.stringify(holders)}`, async () => {
|
|
@@ -105,7 +107,7 @@ describe('revocation authority always includes the principal', () => {
|
|
|
105
107
|
now: AT,
|
|
106
108
|
});
|
|
107
109
|
expect(dr.ok === false && dr.code).toBe('E_REVOKED');
|
|
108
|
-
const ar = await
|
|
110
|
+
const ar = await verifyActionAsOfSignedAt({
|
|
109
111
|
action: action(d, '2026-04-23T06:00:00Z'),
|
|
110
112
|
delegation: d,
|
|
111
113
|
revocations: [rev],
|
|
@@ -115,22 +117,24 @@ describe('revocation authority always includes the principal', () => {
|
|
|
115
117
|
});
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
delegation
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
// A copy of the delegation presented with holders ["principal"] has the same
|
|
121
|
+
// id and signature as one with the agent listed, so it must not undo the
|
|
122
|
+
// agent's own revocation (SPEC §9.5).
|
|
123
|
+
for (const holders of [['principal', 'agent'], ['principal'], [], undefined]) {
|
|
124
|
+
it(`the agent's revocation is honoured with holders=${JSON.stringify(holders)}`, async () => {
|
|
125
|
+
const d = delegation(holders);
|
|
126
|
+
const rev = revocation(d, AGENT, '2026-04-23T00:00:00Z');
|
|
127
|
+
const rr = await verifyRevocation({ envelope: rev, delegation: d, skipSignatureVerification: true });
|
|
128
|
+
expect(rr.ok).toBe(true);
|
|
129
|
+
const dr = await verifyDelegation({
|
|
130
|
+
envelope: d,
|
|
131
|
+
revocations: [rev],
|
|
132
|
+
skipSignatureVerification: true,
|
|
133
|
+
now: AT,
|
|
134
|
+
});
|
|
135
|
+
expect(dr.ok === false && dr.code).toBe('E_REVOKED');
|
|
131
136
|
});
|
|
132
|
-
|
|
133
|
-
});
|
|
137
|
+
}
|
|
134
138
|
|
|
135
139
|
it('a third party is never an authorised revoker', async () => {
|
|
136
140
|
const d = delegation(['principal', 'agent']);
|
|
@@ -150,7 +154,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
150
154
|
const later = '2026-04-23T06:00:00Z';
|
|
151
155
|
|
|
152
156
|
it('an action anchor with no anchor verifier does not outrank a revocation', async () => {
|
|
153
|
-
const r = await
|
|
157
|
+
const r = await verifyActionAsOfSignedAt({
|
|
154
158
|
action: action(d, later, confirmed(1)),
|
|
155
159
|
delegation: d,
|
|
156
160
|
revocations: [rev],
|
|
@@ -160,7 +164,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
160
164
|
});
|
|
161
165
|
|
|
162
166
|
it('an action anchor the verifier rejects does not outrank a revocation', async () => {
|
|
163
|
-
const r = await
|
|
167
|
+
const r = await verifyActionAsOfSignedAt({
|
|
164
168
|
action: action(d, later, confirmed(1)),
|
|
165
169
|
delegation: d,
|
|
166
170
|
revocations: [rev],
|
|
@@ -168,7 +172,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
168
172
|
verifyOtsAnchor: async () => false,
|
|
169
173
|
});
|
|
170
174
|
expect(r.ok === false && r.code).toBe('E_REVOKED');
|
|
171
|
-
const clean = await
|
|
175
|
+
const clean = await verifyActionAsOfSignedAt({
|
|
172
176
|
action: action(d, later, confirmed(1)),
|
|
173
177
|
delegation: d,
|
|
174
178
|
revocations: [],
|
|
@@ -185,14 +189,14 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
185
189
|
|
|
186
190
|
// The ots block is outside the signature: its block is a claim until walked.
|
|
187
191
|
it('reports an unwalked anchor as claimed, and a walked one as confirmed', async () => {
|
|
188
|
-
const unwalked = await
|
|
192
|
+
const unwalked = await verifyActionAsOfSignedAt({
|
|
189
193
|
action: action(d, later, confirmed(1)),
|
|
190
194
|
delegation: d,
|
|
191
195
|
revocations: [],
|
|
192
196
|
skipSignatureVerification: true,
|
|
193
197
|
});
|
|
194
198
|
expect(unwalked.ok && unwalked.anchor.status).toBe('claimed');
|
|
195
|
-
const walked = await
|
|
199
|
+
const walked = await verifyActionAsOfSignedAt({
|
|
196
200
|
action: action(d, later, confirmed(1)),
|
|
197
201
|
delegation: d,
|
|
198
202
|
revocations: [],
|
|
@@ -211,7 +215,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
211
215
|
const a = action(d, later, confirmed(100));
|
|
212
216
|
const revAnchored = revocation(d, PRINCIPAL, '2026-04-23T00:00:00Z', confirmed(200));
|
|
213
217
|
const seen: string[] = [];
|
|
214
|
-
const r = await
|
|
218
|
+
const r = await verifyActionAsOfSignedAt({
|
|
215
219
|
action: a,
|
|
216
220
|
delegation: d,
|
|
217
221
|
revocations: [revAnchored],
|
|
@@ -227,7 +231,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
227
231
|
});
|
|
228
232
|
|
|
229
233
|
it('a verified action anchor against an unanchored revocation falls back to signed times', async () => {
|
|
230
|
-
const r = await
|
|
234
|
+
const r = await verifyActionAsOfSignedAt({
|
|
231
235
|
action: action(d, later, confirmed(1)),
|
|
232
236
|
delegation: d,
|
|
233
237
|
revocations: [rev],
|
|
@@ -235,7 +239,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
235
239
|
verifyOtsAnchor: async () => true,
|
|
236
240
|
});
|
|
237
241
|
expect(r.ok === false && r.code).toBe('E_REVOKED');
|
|
238
|
-
const before = await
|
|
242
|
+
const before = await verifyActionAsOfSignedAt({
|
|
239
243
|
action: action(d, '2026-04-22T18:00:00Z', confirmed(1)),
|
|
240
244
|
delegation: d,
|
|
241
245
|
revocations: [rev],
|
|
@@ -248,7 +252,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
248
252
|
it('a revocation anchor that does not verify cannot move the revocation later', async () => {
|
|
249
253
|
const revFarFuture = revocation(d, PRINCIPAL, '2026-04-23T00:00:00Z', confirmed(10_000_000));
|
|
250
254
|
const a = action(d, later, confirmed(100));
|
|
251
|
-
const r = await
|
|
255
|
+
const r = await verifyActionAsOfSignedAt({
|
|
252
256
|
action: a,
|
|
253
257
|
delegation: d,
|
|
254
258
|
revocations: [revFarFuture],
|
|
@@ -262,7 +266,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
262
266
|
const a = action(d, later, confirmed(300));
|
|
263
267
|
const revAt = (h: number) => revocation(d, PRINCIPAL, '2026-04-23T00:00:00Z', confirmed(h));
|
|
264
268
|
const verifyOtsAnchor = async () => true;
|
|
265
|
-
const earlierRev = await
|
|
269
|
+
const earlierRev = await verifyActionAsOfSignedAt({
|
|
266
270
|
action: a,
|
|
267
271
|
delegation: d,
|
|
268
272
|
revocations: [revAt(300)],
|
|
@@ -270,7 +274,7 @@ describe('revocation priority uses only verified anchors', () => {
|
|
|
270
274
|
verifyOtsAnchor,
|
|
271
275
|
});
|
|
272
276
|
expect(earlierRev.ok === false && earlierRev.code).toBe('E_REVOKED');
|
|
273
|
-
const laterRev = await
|
|
277
|
+
const laterRev = await verifyActionAsOfSignedAt({
|
|
274
278
|
action: a,
|
|
275
279
|
delegation: d,
|
|
276
280
|
revocations: [revAt(301)],
|
|
@@ -106,7 +106,7 @@ describe('the refusal names a query relays answer', () => {
|
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
it('verifyAction', async () => {
|
|
109
|
-
const r = await verifyAction({ action, delegation, skipSignatureVerification: true });
|
|
109
|
+
const r = await verifyAction({ action, delegation, skipSignatureVerification: true, now: AT });
|
|
110
110
|
expect(r.ok === false && r.code).toBe('E_MALFORMED');
|
|
111
111
|
expect(r.ok === false && r.message).toMatch(/"#t": \[id of every link in the chain\]/);
|
|
112
112
|
expect(r.ok === false && r.message).not.toMatch(/#delegation/);
|