@orangecheck/agent-signer 0.5.0 → 0.5.2
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 +3 -3
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +26 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +16 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/index.test.ts +45 -16
- package/src/index.ts +22 -7
package/README.md
CHANGED
|
@@ -116,15 +116,15 @@ Returns an `ActionEnvelope` — a strict extension of an [OC Stamp](https://gith
|
|
|
116
116
|
|
|
117
117
|
```ts
|
|
118
118
|
interface RevokeInput {
|
|
119
|
-
signer: SignerRef; //
|
|
119
|
+
signer: SignerRef; // the delegation's principal or agent
|
|
120
120
|
delegation: DelegationEnvelope;
|
|
121
|
-
reason?: string; // ASCII, <=128 bytes
|
|
121
|
+
reason?: string; // printable ASCII, <=128 bytes
|
|
122
122
|
signedAt?: Date;
|
|
123
123
|
ots?: ActionOts | null;
|
|
124
124
|
}
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
Returns a `RevocationEnvelope`. The function throws
|
|
127
|
+
Returns a `RevocationEnvelope`. The function throws unless the signer is the delegation's principal or its agent, who may always revoke (OC Agent §9.5).
|
|
128
128
|
|
|
129
129
|
## Verification
|
|
130
130
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DelegationBond, RevocationHolder, ValidationOptions, DelegationEnvelope, ActionOts, RevocationEnvelope, ActionEnvelope } from '@orangecheck/agent-core';
|
|
2
|
-
export { ActionEnvelope, ActionOts, AgentErrorCode, DEFAULT_MAX_SKEW_MS, DelegationBond, DelegationEnvelope, REGISTERED_SCOPES, RevocationEnvelope, Scope, ScopeConstraint, VerifyActionAsOfSignedAtInput, VerifyActionInput, canonicalizeScope, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, createReplayGuard, isSubScope, parseScope, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from '@orangecheck/agent-core';
|
|
2
|
+
export { ActionEnvelope, ActionOts, AgentErrorCode, DEFAULT_MAX_SKEW_MS, DelegationBond, DelegationEnvelope, REGISTERED_SCOPES, REVOCATION_REASON_MAX_BYTES, RevocationEnvelope, Scope, ScopeConstraint, VerifyActionAsOfSignedAtInput, VerifyActionInput, authoredReasonProblem, canonicalizeScope, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, createReplayGuard, isSubScope, parseScope, shownReason, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from '@orangecheck/agent-core';
|
|
3
3
|
|
|
4
4
|
interface SignerRef {
|
|
5
5
|
address: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DelegationBond, RevocationHolder, ValidationOptions, DelegationEnvelope, ActionOts, RevocationEnvelope, ActionEnvelope } from '@orangecheck/agent-core';
|
|
2
|
-
export { ActionEnvelope, ActionOts, AgentErrorCode, DEFAULT_MAX_SKEW_MS, DelegationBond, DelegationEnvelope, REGISTERED_SCOPES, RevocationEnvelope, Scope, ScopeConstraint, VerifyActionAsOfSignedAtInput, VerifyActionInput, canonicalizeScope, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, createReplayGuard, isSubScope, parseScope, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from '@orangecheck/agent-core';
|
|
2
|
+
export { ActionEnvelope, ActionOts, AgentErrorCode, DEFAULT_MAX_SKEW_MS, DelegationBond, DelegationEnvelope, REGISTERED_SCOPES, REVOCATION_REASON_MAX_BYTES, RevocationEnvelope, Scope, ScopeConstraint, VerifyActionAsOfSignedAtInput, VerifyActionInput, authoredReasonProblem, canonicalizeScope, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, createReplayGuard, isSubScope, parseScope, shownReason, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from '@orangecheck/agent-core';
|
|
3
3
|
|
|
4
4
|
interface SignerRef {
|
|
5
5
|
address: string;
|
package/dist/index.js
CHANGED
|
@@ -147,17 +147,25 @@ async function signAsAgent(input) {
|
|
|
147
147
|
return envelope;
|
|
148
148
|
}
|
|
149
149
|
async function revoke(input) {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
if (holders.includes("principal")) allowed.add(input.delegation.principal.address);
|
|
153
|
-
if (holders.includes("agent")) allowed.add(input.delegation.agent.address);
|
|
154
|
-
if (!allowed.has(input.signer.address)) {
|
|
150
|
+
const { principal, agent } = input.delegation;
|
|
151
|
+
if (input.signer.address !== principal.address && input.signer.address !== agent.address) {
|
|
155
152
|
throw new Error(
|
|
156
153
|
`revoke: signer ${input.signer.address} not authorized to revoke this delegation`
|
|
157
154
|
);
|
|
158
155
|
}
|
|
159
156
|
const reason = input.reason ?? "";
|
|
160
|
-
|
|
157
|
+
switch (agentCore.authoredReasonProblem(reason)) {
|
|
158
|
+
case null:
|
|
159
|
+
break;
|
|
160
|
+
case "too_long":
|
|
161
|
+
throw new Error(`revoke: reason must be at most ${agentCore.REVOCATION_REASON_MAX_BYTES} bytes`);
|
|
162
|
+
case "not_ascii":
|
|
163
|
+
throw new Error("revoke: reason must be ASCII");
|
|
164
|
+
case "control_character":
|
|
165
|
+
throw new Error("revoke: reason must have no line break or other control character");
|
|
166
|
+
case "not_text":
|
|
167
|
+
throw new Error("revoke: reason must be text");
|
|
168
|
+
}
|
|
161
169
|
const signedAt = isoUtcSeconds(input.signedAt ?? /* @__PURE__ */ new Date());
|
|
162
170
|
const canonInput = {
|
|
163
171
|
address: input.signer.address,
|
|
@@ -207,6 +215,14 @@ Object.defineProperty(exports, "REGISTERED_SCOPES", {
|
|
|
207
215
|
enumerable: true,
|
|
208
216
|
get: function () { return agentCore.REGISTERED_SCOPES; }
|
|
209
217
|
});
|
|
218
|
+
Object.defineProperty(exports, "REVOCATION_REASON_MAX_BYTES", {
|
|
219
|
+
enumerable: true,
|
|
220
|
+
get: function () { return agentCore.REVOCATION_REASON_MAX_BYTES; }
|
|
221
|
+
});
|
|
222
|
+
Object.defineProperty(exports, "authoredReasonProblem", {
|
|
223
|
+
enumerable: true,
|
|
224
|
+
get: function () { return agentCore.authoredReasonProblem; }
|
|
225
|
+
});
|
|
210
226
|
Object.defineProperty(exports, "canonicalizeScope", {
|
|
211
227
|
enumerable: true,
|
|
212
228
|
get: function () { return agentCore.canonicalizeScope; }
|
|
@@ -239,6 +255,10 @@ Object.defineProperty(exports, "parseScope", {
|
|
|
239
255
|
enumerable: true,
|
|
240
256
|
get: function () { return agentCore.parseScope; }
|
|
241
257
|
});
|
|
258
|
+
Object.defineProperty(exports, "shownReason", {
|
|
259
|
+
enumerable: true,
|
|
260
|
+
get: function () { return agentCore.shownReason; }
|
|
261
|
+
});
|
|
242
262
|
Object.defineProperty(exports, "verifyAction", {
|
|
243
263
|
enumerable: true,
|
|
244
264
|
get: function () { return agentCore.verifyAction; }
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["validateScope","parseScope","canonicalizeScopes","computeDelegationId","sealScopes","ENVELOPE_VERSION","hexEncode","sha256","computeActionId","computeRevocationId"],"mappings":";;;;;;AAuGA,eAAsB,iBAAiB,KAAA,EAA2D;AAC9F,EAAA,IAAI,CAAC,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,MAAA,CAAO,WAAW,CAAA,EAAG;AAC5C,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACtE;AACA,EAAA,MAAM,IAAA,GAAO,MAAM,SAAA,IAAa,QAAA;AAChC,EAAA,KAAA,MAAW,CAAA,IAAK,MAAM,MAAA,EAAQA,uBAAA,CAAcC,qBAAW,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,CAAA;AACnE,EAAA,MAAM,MAAA,GAASC,4BAAA,CAAmB,KAAA,CAAM,MAAM,CAAA;AAE9C,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAC3D,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,MAAM,SAAA,EAAW;AACjB,IAAA,aAAA,GAAgB,KAAA,CAAM,SAAA;AAAA,EAC1B,CAAA,MAAO;AACH,IAAA,MAAM,MAAM,KAAA,CAAM,KAAA,IAAS,CAAA,GAAI,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC9C,IAAA,aAAA,GAAgB,IAAI,KAAK,IAAI,IAAA,CAAK,QAAQ,CAAA,CAAE,OAAA,KAAY,GAAG,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,SAAA,GAAY,cAAc,aAAa,CAAA;AAE7C,EAAA,MAAM,UAAA,GAAa,IAAI,IAAA,CAAK,SAAS,CAAA,CAAE,OAAA,EAAQ,GAAI,IAAI,IAAA,CAAK,QAAQ,CAAA,CAAE,OAAA,EAAQ;AAC9E,EAAA,IAAI,UAAA,IAAc,CAAA,EAAG,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAC3F,EAAA,IAAI,UAAA,GAAa,GAAA,GAAM,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA,EAAM;AACxC,IAAA,MAAM,IAAI,MAAM,gEAAgE,CAAA;AAAA,EACpF;AAEA,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,IAAS,SAAA,CAAU,EAAE,CAAA;AACzC,EAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,KAAK,CAAA,EAAG;AAC/B,IAAA,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAAA,EAC5E;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,IAAQ,IAAA;AAC3B,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,IAAK,IAAA,CAAK,OAAO,CAAA,EAAG;AAC/C,MAAA,MAAM,IAAI,MAAM,4DAA4D,CAAA;AAAA,IAChF;AACA,IAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,IAAA,CAAK,cAAc,CAAA,EAAG;AAC7C,MAAA,MAAM,IAAI,MAAM,sEAAsE,CAAA;AAAA,IAC1F;AAAA,EACJ;AAEA,EAAA,MAAM,UAAA,GAAa;AAAA,IACf,SAAA,EAAW,MAAM,SAAA,CAAU,OAAA;AAAA,IAC3B,OAAO,KAAA,CAAM,YAAA;AAAA,IACb,MAAA;AAAA,IACA,SAAA,EAAW,MAAM,IAAA,IAAQ,CAAA;AAAA,IACzB,gBAAA,EAAkB,MAAM,cAAA,IAAkB,MAAA;AAAA,IAC1C,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,SAAA;AAAA,IACZ;AAAA,GACJ;AACA,EAAA,MAAM,EAAA,GAAKC,8BAAoB,UAAU,CAAA;AAKzC,EAAA,IAAI,eAAA;AACJ,EAAA,IAAI,MAAM,aAAA,EAAe;AACrB,IAAA,IAAI,CAAC,MAAM,aAAA,CAAc,UAAA,IAAc,MAAM,aAAA,CAAc,UAAA,CAAW,WAAW,CAAA,EAAG;AAChF,MAAA,MAAM,IAAI,KAAA;AAAA,QACN;AAAA,OACJ;AAAA,IACJ;AACA,IAAA,eAAA,GAAkB,MAAMC,oBAAA,CAAW;AAAA,MAC/B,MAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACJ,OAAA,EAAS,MAAM,SAAA,CAAU,OAAA;AAAA,QACzB,WAAA,EAAa,MAAM,SAAA,CAAU;AAAA,OACjC;AAAA,MACA,UAAA,EAAY,MAAM,aAAA,CAAc,UAAA;AAAA,MAChC,GAAI,KAAA,CAAM,aAAA,CAAc,IAAA,KAAS,MAAA,IAAa;AAAA,QAC1C,IAAA,EAAM,MAAM,aAAA,CAAc;AAAA,OAC9B;AAAA,MACA,GAAI,KAAA,CAAM,aAAA,CAAc,iBAAA,KAAsB,MAAA,IAAa;AAAA,QACvD,SAAA,EAAW,MAAM,aAAA,CAAc;AAAA;AACnC,KACH,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,SAAA,CAAU,YAAY,EAAE,CAAA;AAErD,EAAA,MAAM,QAAA,GAA+B;AAAA,IACjC,CAAA,EAAGC,0BAAA;AAAA,IACH,IAAA,EAAM,kBAAA;AAAA,IACN,EAAA;AAAA,IACA,WAAW,EAAE,OAAA,EAAS,MAAM,SAAA,CAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,IAC7D,OAAO,EAAE,OAAA,EAAS,KAAA,CAAM,YAAA,EAAc,KAAK,QAAA,EAAS;AAAA;AAAA,IAEpD,GAAI,eAAA,GAAkB,EAAE,kBAAkB,eAAA,EAAgB,GAAI,EAAE,MAAA,EAAO;AAAA,IACvE,IAAA;AAAA,IACA,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,SAAA;AAAA,IACZ,KAAA;AAAA,IACA,UAAA,EAAY;AAAA,MACR,OAAA,EAAS,KAAA,CAAM,iBAAA,IAAqB,CAAC,WAAW,CAAA;AAAA,MAChD,GAAA,EAAK,MAAM,aAAA,IAAiB;AAAA,KAChC;AAAA,IACA,GAAA,EAAK;AAAA,MACD,GAAA,EAAK,QAAA;AAAA,MACL,MAAA,EAAQ,MAAM,SAAA,CAAU,OAAA;AAAA,MACxB,KAAA,EAAO;AAAA;AACX,GACJ;AACA,EAAA,OAAO,QAAA;AACX;AAoBA,eAAsB,YAAY,KAAA,EAAkD;AAChF,EAAA,IAAI,MAAM,KAAA,CAAM,OAAA,KAAY,KAAA,CAAM,UAAA,CAAW,MAAM,OAAA,EAAS;AACxD,IAAA,MAAM,IAAI,MAAM,qEAAqE,CAAA;AAAA,EACzF;AAEA,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,KAAA,CAAM,mBAAmB,UAAA,EAAY;AACrC,IAAA,WAAA,GAAc,SAAA,GAAYC,mBAAA,CAAUC,aAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AACzD,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,UAAA;AAAA,EAClC,CAAA,MAAO;AACH,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,SAAS,CAAA,IAAK,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,MAAA,KAAW,SAAA,CAAU,SAAS,EAAA,EAAI;AAClG,MAAA,MAAM,IAAI,MAAM,sEAAsE,CAAA;AAAA,IAC1F;AACA,IAAA,WAAA,GAAc,MAAM,OAAA,CAAQ,IAAA;AAC5B,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,MAAA;AAC9B,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,aAAa,CAAA,IAAK,gBAAgB,CAAA,EAAG;AACvD,MAAA,MAAM,IAAI,MAAM,4DAA4D,CAAA;AAAA,IAChF;AAAA,EACJ;AAEA,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAE3D,EAAA,MAAM,UAAA,GAAa;AAAA,IACf,OAAA,EAAS,MAAM,KAAA,CAAM,OAAA;AAAA,IACrB,YAAA,EAAc,WAAA;AAAA,IACd,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAc,KAAA,CAAM,IAAA;AAAA,IACpB,SAAA,EAAW,QAAA;AAAA,IACX,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,iBAAiB,KAAA,CAAM;AAAA,GAC3B;AACA,EAAA,MAAM,EAAA,GAAKC,0BAAgB,UAAU,CAAA;AAErC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,KAAA,CAAM,YAAY,EAAE,CAAA;AAEjD,EAAA,MAAM,QAAA,GAA2B;AAAA,IAC7B,CAAA,EAAGH,0BAAA;AAAA,IACH,IAAA,EAAM,cAAA;AAAA,IACN,EAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,MAAA,EAAQ,aAAA;AAAA,MACR,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,GAAA,EAAK,MAAM,GAAA,IAAO;AAAA,KACtB;AAAA,IACA,QAAQ,EAAE,OAAA,EAAS,MAAM,KAAA,CAAM,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,IACtD,SAAA,EAAW,QAAA;AAAA,IACX,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,iBAAiB,KAAA,CAAM,cAAA;AAAA,IACvB,GAAA,EAAK,MAAM,GAAA,IAAO,IAAA;AAAA,IAClB,GAAA,EAAK,EAAE,GAAA,EAAK,QAAA,EAAU,QAAQ,KAAA,CAAM,KAAA,CAAM,OAAA,EAAS,KAAA,EAAO,QAAA;AAAS,GACvE;AACA,EAAA,OAAO,QAAA;AACX;AAgBA,eAAsB,OAAO,KAAA,EAAiD;AAC1E,EAAA,MAAM,UAAU,KAAA,CAAM,UAAA,CAAW,UAAA,EAAY,OAAA,IAAW,CAAC,WAAW,CAAA;AACpE,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,IAAI,OAAA,CAAQ,SAAS,WAAW,CAAA,UAAW,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,SAAA,CAAU,OAAO,CAAA;AACjF,EAAA,IAAI,OAAA,CAAQ,SAAS,OAAO,CAAA,UAAW,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM,OAAO,CAAA;AACzE,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,OAAO,CAAA,EAAG;AACpC,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,eAAA,EAAkB,KAAA,CAAM,MAAA,CAAO,OAAO,CAAA,yCAAA;AAAA,KAC1C;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,EAAA;AAC/B,EAAA,IAAI,OAAO,MAAA,GAAS,GAAA,EAAK,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAE7E,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAC3D,EAAA,MAAM,UAAA,GAAa;AAAA,IACf,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAA,IACtB,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,MAAA;AAAA,IACA,SAAA,EAAW;AAAA,GACf;AACA,EAAA,MAAM,EAAA,GAAKI,8BAAoB,UAAU,CAAA;AAEzC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,CAAO,YAAY,EAAE,CAAA;AAElD,EAAA,MAAM,QAAA,GAA+B;AAAA,IACjC,CAAA,EAAGJ,0BAAA;AAAA,IACH,IAAA,EAAM,kBAAA;AAAA,IACN,EAAA;AAAA,IACA,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,QAAQ,EAAE,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,IACvD,MAAA;AAAA,IACA,SAAA,EAAW,QAAA;AAAA,IACX,GAAA,EAAK,MAAM,GAAA,IAAO,IAAA;AAAA,IAClB,GAAA,EAAK,EAAE,GAAA,EAAK,QAAA,EAAU,QAAQ,KAAA,CAAM,MAAA,CAAO,OAAA,EAAS,KAAA,EAAO,QAAA;AAAS,GACxE;AACA,EAAA,OAAO,QAAA;AACX;AAuCA,SAAS,cAAc,CAAA,EAAiB;AACpC,EAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,WAAW,GAAG,CAAA;AACjD;AAEA,SAAS,UAAU,KAAA,EAAuB;AACtC,EAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,KAAK,CAAA;AAChC,EAAA,MAAM,IAAyB,UAAA,CAA8C,MAAA;AAC7E,EAAA,IAAI,CAAA,IAAK,OAAO,CAAA,CAAE,eAAA,KAAoB,UAAA,EAAY;AAC9C,IAAA,CAAA,CAAE,gBAAgB,GAAG,CAAA;AAAA,EACzB,CAAA,MAAO;AAEH,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EAC3E;AACA,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,IAAA,MAAM,CAAA,GAAI,IAAI,CAAC,CAAA;AACf,IAAA,GAAA,IAAO,EAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAAA,EACzC;AACA,EAAA,OAAO,GAAA;AACX","file":"index.js","sourcesContent":["// @orangecheck/agent-signer — high-level API for producing OC Agent envelopes.\n//\n// createDelegation — principal signs a scoped grant to an agent address.\n// signAsAgent — agent produces an agent-action envelope over content.\n// revoke — principal (or agent, if authorized) burns a delegation.\n//\n// Each function wraps @orangecheck/agent-core's canonical-message + id\n// computation with a caller-supplied `signMessage` wallet adapter. Nothing here\n// touches the network — OTS anchoring and Nostr publication are separate steps\n// the caller wires up.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n ENVELOPE_VERSION,\n hexEncode,\n parseScope,\n sealScopes,\n validateScope,\n} from '@orangecheck/agent-core';\nimport type {\n ScopesEncryptedEnvelope,\n ValidationOptions,\n} from '@orangecheck/agent-core';\nimport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n RevocationHolder,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared wallet-adapter shape (matches `@orangecheck/stamp-core` StampInput).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignerRef {\n address: string;\n /** Returns a base64 BIP-322 signature over the given message as UTF-8 bytes. */\n signMessage: (msg: string) => Promise<string>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createDelegation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CreateDelegationInput {\n principal: SignerRef;\n agentAddress: string;\n scopes: string[];\n bond?: DelegationBond | null;\n issuedAt?: Date;\n /** Duration from issuedAt in ms. Default 7 days. MAX 365 days per SPEC §4.4. */\n ttlMs?: number;\n /** Explicit expires_at overrides ttlMs. */\n expiresAt?: Date;\n /** 32-hex. Defaults to crypto.getRandomValues(16-byte) rendered as hex. */\n nonce?: string;\n /** Default ['principal']. */\n revocationHolders?: RevocationHolder[];\n /** Optional Nostr pointer to a revocation event. Non-cryptographic. */\n revocationRef?: string | null;\n /** Default 'strict'. 'permissive' allows unregistered scopes / constraint keys. */\n scopeMode?: ValidationOptions['mode'];\n /**\n * v1.2 private-scope (PRIVATE-SCOPE.md). When supplied, the resulting\n * delegation envelope omits the public `scopes` field and instead carries\n * `scopes_encrypted` — an OC Lock envelope wrapping the canonical scope\n * list, sealed to one or more recipient device keys. The principal signs\n * BOTH the inner OC Lock envelope (via the `signMessage` callback already\n * provided on `principal`) AND the outer OC Agent canonical message.\n *\n * Recipients MUST include at least the agent's device. Additional\n * recipients (compliance auditors, third-party verifiers) are optional.\n */\n privateScopes?: {\n /**\n * Devices authorized to read the scope set. Each is `{ address,\n * device_id, device_pk }` matching OC Lock's `DeviceRecord` shape.\n * The same `signMessage` callback on `principal` is used to sign the\n * inner OC Lock envelope.\n */\n recipients: PrivateScopeRecipient[];\n /** Optional human hint stored in the OC Lock envelope. */\n hint?: string;\n /** Optional expiry on the OC Lock envelope. Independent of the\n * delegation's expires_at; usually left unset. */\n envelopeExpiresAt?: Date | null;\n };\n}\n\n/** Mirror of @orangecheck/lock-core's DeviceRecord. */\nexport interface PrivateScopeRecipient {\n address: string;\n device_id: string;\n /** 32-byte hex X25519 public key. */\n device_pk: string;\n}\n\nexport async function createDelegation(input: CreateDelegationInput): Promise<DelegationEnvelope> {\n if (!input.scopes || input.scopes.length === 0) {\n throw new Error('createDelegation: at least one scope is required');\n }\n const mode = input.scopeMode ?? 'strict';\n for (const s of input.scopes) validateScope(parseScope(s), { mode });\n const scopes = canonicalizeScopes(input.scopes);\n\n const issuedAt = isoUtcSeconds(input.issuedAt ?? new Date());\n let expiresAtDate: Date;\n if (input.expiresAt) {\n expiresAtDate = input.expiresAt;\n } else {\n const ttl = input.ttlMs ?? 7 * 24 * 60 * 60 * 1000;\n expiresAtDate = new Date(new Date(issuedAt).getTime() + ttl);\n }\n const expiresAt = isoUtcSeconds(expiresAtDate);\n\n const durationMs = new Date(expiresAt).getTime() - new Date(issuedAt).getTime();\n if (durationMs <= 0) throw new Error('createDelegation: expires_at must be after issued_at');\n if (durationMs > 365 * 24 * 60 * 60 * 1000) {\n throw new Error('createDelegation: delegation duration must not exceed 365 days');\n }\n\n const nonce = input.nonce ?? randomHex(16);\n if (!/^[0-9a-f]{32}$/.test(nonce)) {\n throw new Error('createDelegation: nonce must be 32 lowercase hex chars');\n }\n\n const bond = input.bond ?? null;\n if (bond) {\n if (!Number.isInteger(bond.sats) || bond.sats < 0) {\n throw new Error('createDelegation: bond.sats must be a non-negative integer');\n }\n if (!/^[0-9a-f]{64}$/.test(bond.attestation_id)) {\n throw new Error('createDelegation: bond.attestation_id must be 64 lowercase hex chars');\n }\n }\n\n const canonInput = {\n principal: input.principal.address,\n agent: input.agentAddress,\n scopes,\n bond_sats: bond?.sats ?? 0,\n bond_attestation: bond?.attestation_id ?? 'none',\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n };\n const id = computeDelegationId(canonInput);\n\n // v1.2 private-scope: seal BEFORE the principal's outer signature so the\n // user's wallet sees both prompts in a deterministic order (inner Lock\n // sig first, outer OC Agent sig second).\n let scopesEncrypted: ScopesEncryptedEnvelope | undefined;\n if (input.privateScopes) {\n if (!input.privateScopes.recipients || input.privateScopes.recipients.length === 0) {\n throw new Error(\n 'createDelegation: privateScopes.recipients must contain at least the agent device'\n );\n }\n scopesEncrypted = await sealScopes({\n scopes,\n sender: {\n address: input.principal.address,\n signMessage: input.principal.signMessage,\n },\n recipients: input.privateScopes.recipients,\n ...(input.privateScopes.hint !== undefined && {\n hint: input.privateScopes.hint,\n }),\n ...(input.privateScopes.envelopeExpiresAt !== undefined && {\n expiresAt: input.privateScopes.envelopeExpiresAt,\n }),\n });\n }\n\n const sigValue = await input.principal.signMessage(id);\n\n const envelope: DelegationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-delegation',\n id,\n principal: { address: input.principal.address, alg: 'bip322' },\n agent: { address: input.agentAddress, alg: 'bip322' },\n // Public OR private mode — never both. PRIVATE-SCOPE.md §1.1.\n ...(scopesEncrypted ? { scopes_encrypted: scopesEncrypted } : { scopes }),\n bond,\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n revocation: {\n holders: input.revocationHolders ?? ['principal'],\n ref: input.revocationRef ?? null,\n },\n sig: {\n alg: 'bip322',\n pubkey: input.principal.address,\n value: sigValue,\n },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// signAsAgent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignAsAgentInput {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n /** Raw bytes to stamp, or a pre-computed { hash, length } pair. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n /** Which scope from delegation.scopes is being exercised (or a narrower sub-scope). */\n scopeExercised: string;\n ref?: string | null;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function signAsAgent(input: SignAsAgentInput): Promise<ActionEnvelope> {\n if (input.agent.address !== input.delegation.agent.address) {\n throw new Error('signAsAgent: signer address does not match delegation.agent.address');\n }\n\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n if (!input.content.hash.startsWith('sha256:') || input.content.hash.length !== 'sha256:'.length + 64) {\n throw new Error('signAsAgent: content.hash must be \"sha256:\" + 64 lowercase hex chars');\n }\n contentHash = input.content.hash;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new Error('signAsAgent: content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canonInput = {\n address: input.agent.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n };\n const id = computeActionId(canonInput);\n\n const sigValue = await input.agent.signMessage(id);\n\n const envelope: ActionEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-action',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: { address: input.agent.address, alg: 'bip322' },\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.agent.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// revoke\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevokeInput {\n signer: SignerRef;\n delegation: DelegationEnvelope;\n /** Short ASCII rationale. Defaults to empty string. */\n reason?: string;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function revoke(input: RevokeInput): Promise<RevocationEnvelope> {\n const holders = input.delegation.revocation?.holders ?? ['principal'];\n const allowed = new Set<string>();\n if (holders.includes('principal')) allowed.add(input.delegation.principal.address);\n if (holders.includes('agent')) allowed.add(input.delegation.agent.address);\n if (!allowed.has(input.signer.address)) {\n throw new Error(\n `revoke: signer ${input.signer.address} not authorized to revoke this delegation`\n );\n }\n\n const reason = input.reason ?? '';\n if (reason.length > 128) throw new Error('revoke: reason must be <=128 bytes');\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n const canonInput = {\n address: input.signer.address,\n delegation_id: input.delegation.id,\n reason,\n signed_at: signedAt,\n };\n const id = computeRevocationId(canonInput);\n\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: RevocationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-revocation',\n id,\n delegation_id: input.delegation.id,\n signer: { address: input.signer.address, alg: 'bip322' },\n reason,\n signed_at: signedAt,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.signer.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Re-exports for convenience\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n verifyAction,\n verifyActionAsOfSignedAt,\n createReplayGuard,\n DEFAULT_MAX_SKEW_MS,\n verifyDelegation,\n verifyRevocation,\n parseScope,\n isSubScope,\n canonicalizeScope,\n REGISTERED_SCOPES,\n} from '@orangecheck/agent-core';\nexport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n Scope,\n ScopeConstraint,\n AgentErrorCode,\n VerifyActionInput,\n VerifyActionAsOfSignedAtInput,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction isoUtcSeconds(d: Date): string {\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n\nfunction randomHex(bytes: number): string {\n const buf = new Uint8Array(bytes);\n const c: Crypto | undefined = (globalThis as unknown as { crypto?: Crypto }).crypto;\n if (c && typeof c.getRandomValues === 'function') {\n c.getRandomValues(buf);\n } else {\n // Non-browser fallback. Node ≥18 exposes globalThis.crypto, so this is unlikely to fire.\n for (let i = 0; i < bytes; i++) buf[i] = Math.floor(Math.random() * 256);\n }\n let out = '';\n for (let i = 0; i < bytes; i++) {\n const b = buf[i]!;\n out += b.toString(16).padStart(2, '0');\n }\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["validateScope","parseScope","canonicalizeScopes","computeDelegationId","sealScopes","ENVELOPE_VERSION","hexEncode","sha256","computeActionId","authoredReasonProblem","REVOCATION_REASON_MAX_BYTES","computeRevocationId"],"mappings":"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","file":"index.js","sourcesContent":["// @orangecheck/agent-signer — high-level API for producing OC Agent envelopes.\n//\n// createDelegation — principal signs a scoped grant to an agent address.\n// signAsAgent — agent produces an agent-action envelope over content.\n// revoke — principal (or agent, if authorized) burns a delegation.\n//\n// Each function wraps @orangecheck/agent-core's canonical-message + id\n// computation with a caller-supplied `signMessage` wallet adapter. Nothing here\n// touches the network — OTS anchoring and Nostr publication are separate steps\n// the caller wires up.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n ENVELOPE_VERSION,\n hexEncode,\n authoredReasonProblem,\n parseScope,\n REVOCATION_REASON_MAX_BYTES,\n sealScopes,\n validateScope,\n} from '@orangecheck/agent-core';\nimport type {\n ScopesEncryptedEnvelope,\n ValidationOptions,\n} from '@orangecheck/agent-core';\nimport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n RevocationHolder,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared wallet-adapter shape (matches `@orangecheck/stamp-core` StampInput).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignerRef {\n address: string;\n /** Returns a base64 BIP-322 signature over the given message as UTF-8 bytes. */\n signMessage: (msg: string) => Promise<string>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createDelegation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CreateDelegationInput {\n principal: SignerRef;\n agentAddress: string;\n scopes: string[];\n bond?: DelegationBond | null;\n issuedAt?: Date;\n /** Duration from issuedAt in ms. Default 7 days. MAX 365 days per SPEC §4.4. */\n ttlMs?: number;\n /** Explicit expires_at overrides ttlMs. */\n expiresAt?: Date;\n /** 32-hex. Defaults to crypto.getRandomValues(16-byte) rendered as hex. */\n nonce?: string;\n /** Default ['principal']. */\n revocationHolders?: RevocationHolder[];\n /** Optional Nostr pointer to a revocation event. Non-cryptographic. */\n revocationRef?: string | null;\n /** Default 'strict'. 'permissive' allows unregistered scopes / constraint keys. */\n scopeMode?: ValidationOptions['mode'];\n /**\n * v1.2 private-scope (PRIVATE-SCOPE.md). When supplied, the resulting\n * delegation envelope omits the public `scopes` field and instead carries\n * `scopes_encrypted` — an OC Lock envelope wrapping the canonical scope\n * list, sealed to one or more recipient device keys. The principal signs\n * BOTH the inner OC Lock envelope (via the `signMessage` callback already\n * provided on `principal`) AND the outer OC Agent canonical message.\n *\n * Recipients MUST include at least the agent's device. Additional\n * recipients (compliance auditors, third-party verifiers) are optional.\n */\n privateScopes?: {\n /**\n * Devices authorized to read the scope set. Each is `{ address,\n * device_id, device_pk }` matching OC Lock's `DeviceRecord` shape.\n * The same `signMessage` callback on `principal` is used to sign the\n * inner OC Lock envelope.\n */\n recipients: PrivateScopeRecipient[];\n /** Optional human hint stored in the OC Lock envelope. */\n hint?: string;\n /** Optional expiry on the OC Lock envelope. Independent of the\n * delegation's expires_at; usually left unset. */\n envelopeExpiresAt?: Date | null;\n };\n}\n\n/** Mirror of @orangecheck/lock-core's DeviceRecord. */\nexport interface PrivateScopeRecipient {\n address: string;\n device_id: string;\n /** 32-byte hex X25519 public key. */\n device_pk: string;\n}\n\nexport async function createDelegation(input: CreateDelegationInput): Promise<DelegationEnvelope> {\n if (!input.scopes || input.scopes.length === 0) {\n throw new Error('createDelegation: at least one scope is required');\n }\n const mode = input.scopeMode ?? 'strict';\n for (const s of input.scopes) validateScope(parseScope(s), { mode });\n const scopes = canonicalizeScopes(input.scopes);\n\n const issuedAt = isoUtcSeconds(input.issuedAt ?? new Date());\n let expiresAtDate: Date;\n if (input.expiresAt) {\n expiresAtDate = input.expiresAt;\n } else {\n const ttl = input.ttlMs ?? 7 * 24 * 60 * 60 * 1000;\n expiresAtDate = new Date(new Date(issuedAt).getTime() + ttl);\n }\n const expiresAt = isoUtcSeconds(expiresAtDate);\n\n const durationMs = new Date(expiresAt).getTime() - new Date(issuedAt).getTime();\n if (durationMs <= 0) throw new Error('createDelegation: expires_at must be after issued_at');\n if (durationMs > 365 * 24 * 60 * 60 * 1000) {\n throw new Error('createDelegation: delegation duration must not exceed 365 days');\n }\n\n const nonce = input.nonce ?? randomHex(16);\n if (!/^[0-9a-f]{32}$/.test(nonce)) {\n throw new Error('createDelegation: nonce must be 32 lowercase hex chars');\n }\n\n const bond = input.bond ?? null;\n if (bond) {\n if (!Number.isInteger(bond.sats) || bond.sats < 0) {\n throw new Error('createDelegation: bond.sats must be a non-negative integer');\n }\n if (!/^[0-9a-f]{64}$/.test(bond.attestation_id)) {\n throw new Error('createDelegation: bond.attestation_id must be 64 lowercase hex chars');\n }\n }\n\n const canonInput = {\n principal: input.principal.address,\n agent: input.agentAddress,\n scopes,\n bond_sats: bond?.sats ?? 0,\n bond_attestation: bond?.attestation_id ?? 'none',\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n };\n const id = computeDelegationId(canonInput);\n\n // v1.2 private-scope: seal BEFORE the principal's outer signature so the\n // user's wallet sees both prompts in a deterministic order (inner Lock\n // sig first, outer OC Agent sig second).\n let scopesEncrypted: ScopesEncryptedEnvelope | undefined;\n if (input.privateScopes) {\n if (!input.privateScopes.recipients || input.privateScopes.recipients.length === 0) {\n throw new Error(\n 'createDelegation: privateScopes.recipients must contain at least the agent device'\n );\n }\n scopesEncrypted = await sealScopes({\n scopes,\n sender: {\n address: input.principal.address,\n signMessage: input.principal.signMessage,\n },\n recipients: input.privateScopes.recipients,\n ...(input.privateScopes.hint !== undefined && {\n hint: input.privateScopes.hint,\n }),\n ...(input.privateScopes.envelopeExpiresAt !== undefined && {\n expiresAt: input.privateScopes.envelopeExpiresAt,\n }),\n });\n }\n\n const sigValue = await input.principal.signMessage(id);\n\n const envelope: DelegationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-delegation',\n id,\n principal: { address: input.principal.address, alg: 'bip322' },\n agent: { address: input.agentAddress, alg: 'bip322' },\n // Public OR private mode — never both. PRIVATE-SCOPE.md §1.1.\n ...(scopesEncrypted ? { scopes_encrypted: scopesEncrypted } : { scopes }),\n bond,\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n revocation: {\n holders: input.revocationHolders ?? ['principal'],\n ref: input.revocationRef ?? null,\n },\n sig: {\n alg: 'bip322',\n pubkey: input.principal.address,\n value: sigValue,\n },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// signAsAgent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignAsAgentInput {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n /** Raw bytes to stamp, or a pre-computed { hash, length } pair. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n /** Which scope from delegation.scopes is being exercised (or a narrower sub-scope). */\n scopeExercised: string;\n ref?: string | null;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function signAsAgent(input: SignAsAgentInput): Promise<ActionEnvelope> {\n if (input.agent.address !== input.delegation.agent.address) {\n throw new Error('signAsAgent: signer address does not match delegation.agent.address');\n }\n\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n if (!input.content.hash.startsWith('sha256:') || input.content.hash.length !== 'sha256:'.length + 64) {\n throw new Error('signAsAgent: content.hash must be \"sha256:\" + 64 lowercase hex chars');\n }\n contentHash = input.content.hash;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new Error('signAsAgent: content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canonInput = {\n address: input.agent.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n };\n const id = computeActionId(canonInput);\n\n const sigValue = await input.agent.signMessage(id);\n\n const envelope: ActionEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-action',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: { address: input.agent.address, alg: 'bip322' },\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.agent.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// revoke\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevokeInput {\n signer: SignerRef;\n delegation: DelegationEnvelope;\n /** Printable ASCII, at most 128 bytes: SPEC §9.1 allows any ASCII; we write no control character. Defaults to \"\". */\n reason?: string;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function revoke(input: RevokeInput): Promise<RevocationEnvelope> {\n // The principal and the agent may always revoke (SPEC §9.5).\n const { principal, agent } = input.delegation;\n if (input.signer.address !== principal.address && input.signer.address !== agent.address) {\n throw new Error(\n `revoke: signer ${input.signer.address} not authorized to revoke this delegation`\n );\n }\n\n const reason = input.reason ?? '';\n // SPEC §9.1: a signer writes printable ASCII of at most 128 bytes.\n switch (authoredReasonProblem(reason)) {\n case null:\n break;\n case 'too_long':\n throw new Error(`revoke: reason must be at most ${REVOCATION_REASON_MAX_BYTES} bytes`);\n case 'not_ascii':\n throw new Error('revoke: reason must be ASCII');\n case 'control_character':\n throw new Error('revoke: reason must have no line break or other control character');\n case 'not_text':\n throw new Error('revoke: reason must be text');\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n const canonInput = {\n address: input.signer.address,\n delegation_id: input.delegation.id,\n reason,\n signed_at: signedAt,\n };\n const id = computeRevocationId(canonInput);\n\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: RevocationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-revocation',\n id,\n delegation_id: input.delegation.id,\n signer: { address: input.signer.address, alg: 'bip322' },\n reason,\n signed_at: signedAt,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.signer.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Re-exports for convenience\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n verifyAction,\n verifyActionAsOfSignedAt,\n createReplayGuard,\n DEFAULT_MAX_SKEW_MS,\n verifyDelegation,\n verifyRevocation,\n parseScope,\n isSubScope,\n canonicalizeScope,\n REGISTERED_SCOPES,\n authoredReasonProblem,\n REVOCATION_REASON_MAX_BYTES,\n shownReason,\n} from '@orangecheck/agent-core';\nexport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n Scope,\n ScopeConstraint,\n AgentErrorCode,\n VerifyActionInput,\n VerifyActionAsOfSignedAtInput,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction isoUtcSeconds(d: Date): string {\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n\nfunction randomHex(bytes: number): string {\n const buf = new Uint8Array(bytes);\n const c: Crypto | undefined = (globalThis as unknown as { crypto?: Crypto }).crypto;\n if (c && typeof c.getRandomValues === 'function') {\n c.getRandomValues(buf);\n } else {\n // Non-browser fallback. Node ≥18 exposes globalThis.crypto, so this is unlikely to fire.\n for (let i = 0; i < bytes; i++) buf[i] = Math.floor(Math.random() * 256);\n }\n let out = '';\n for (let i = 0; i < bytes; i++) {\n const b = buf[i]!;\n out += b.toString(16).padStart(2, '0');\n }\n return out;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sha256 } from '@noble/hashes/sha256';
|
|
2
|
-
import { validateScope, parseScope, canonicalizeScopes, computeDelegationId, sealScopes, ENVELOPE_VERSION, hexEncode, computeActionId, computeRevocationId } from '@orangecheck/agent-core';
|
|
3
|
-
export { DEFAULT_MAX_SKEW_MS, REGISTERED_SCOPES, canonicalizeScope, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, createReplayGuard, isSubScope, parseScope, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from '@orangecheck/agent-core';
|
|
2
|
+
import { validateScope, parseScope, canonicalizeScopes, computeDelegationId, sealScopes, ENVELOPE_VERSION, hexEncode, computeActionId, authoredReasonProblem, REVOCATION_REASON_MAX_BYTES, computeRevocationId } from '@orangecheck/agent-core';
|
|
3
|
+
export { DEFAULT_MAX_SKEW_MS, REGISTERED_SCOPES, REVOCATION_REASON_MAX_BYTES, authoredReasonProblem, canonicalizeScope, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, createReplayGuard, isSubScope, parseScope, shownReason, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from '@orangecheck/agent-core';
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
async function createDelegation(input) {
|
|
@@ -146,17 +146,25 @@ async function signAsAgent(input) {
|
|
|
146
146
|
return envelope;
|
|
147
147
|
}
|
|
148
148
|
async function revoke(input) {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
if (holders.includes("principal")) allowed.add(input.delegation.principal.address);
|
|
152
|
-
if (holders.includes("agent")) allowed.add(input.delegation.agent.address);
|
|
153
|
-
if (!allowed.has(input.signer.address)) {
|
|
149
|
+
const { principal, agent } = input.delegation;
|
|
150
|
+
if (input.signer.address !== principal.address && input.signer.address !== agent.address) {
|
|
154
151
|
throw new Error(
|
|
155
152
|
`revoke: signer ${input.signer.address} not authorized to revoke this delegation`
|
|
156
153
|
);
|
|
157
154
|
}
|
|
158
155
|
const reason = input.reason ?? "";
|
|
159
|
-
|
|
156
|
+
switch (authoredReasonProblem(reason)) {
|
|
157
|
+
case null:
|
|
158
|
+
break;
|
|
159
|
+
case "too_long":
|
|
160
|
+
throw new Error(`revoke: reason must be at most ${REVOCATION_REASON_MAX_BYTES} bytes`);
|
|
161
|
+
case "not_ascii":
|
|
162
|
+
throw new Error("revoke: reason must be ASCII");
|
|
163
|
+
case "control_character":
|
|
164
|
+
throw new Error("revoke: reason must have no line break or other control character");
|
|
165
|
+
case "not_text":
|
|
166
|
+
throw new Error("revoke: reason must be text");
|
|
167
|
+
}
|
|
160
168
|
const signedAt = isoUtcSeconds(input.signedAt ?? /* @__PURE__ */ new Date());
|
|
161
169
|
const canonInput = {
|
|
162
170
|
address: input.signer.address,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAuGA,eAAsB,iBAAiB,KAAA,EAA2D;AAC9F,EAAA,IAAI,CAAC,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,MAAA,CAAO,WAAW,CAAA,EAAG;AAC5C,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACtE;AACA,EAAA,MAAM,IAAA,GAAO,MAAM,SAAA,IAAa,QAAA;AAChC,EAAA,KAAA,MAAW,CAAA,IAAK,MAAM,MAAA,EAAQ,aAAA,CAAc,WAAW,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,CAAA;AACnE,EAAA,MAAM,MAAA,GAAS,kBAAA,CAAmB,KAAA,CAAM,MAAM,CAAA;AAE9C,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAC3D,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,MAAM,SAAA,EAAW;AACjB,IAAA,aAAA,GAAgB,KAAA,CAAM,SAAA;AAAA,EAC1B,CAAA,MAAO;AACH,IAAA,MAAM,MAAM,KAAA,CAAM,KAAA,IAAS,CAAA,GAAI,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC9C,IAAA,aAAA,GAAgB,IAAI,KAAK,IAAI,IAAA,CAAK,QAAQ,CAAA,CAAE,OAAA,KAAY,GAAG,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,SAAA,GAAY,cAAc,aAAa,CAAA;AAE7C,EAAA,MAAM,UAAA,GAAa,IAAI,IAAA,CAAK,SAAS,CAAA,CAAE,OAAA,EAAQ,GAAI,IAAI,IAAA,CAAK,QAAQ,CAAA,CAAE,OAAA,EAAQ;AAC9E,EAAA,IAAI,UAAA,IAAc,CAAA,EAAG,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAC3F,EAAA,IAAI,UAAA,GAAa,GAAA,GAAM,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA,EAAM;AACxC,IAAA,MAAM,IAAI,MAAM,gEAAgE,CAAA;AAAA,EACpF;AAEA,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,IAAS,SAAA,CAAU,EAAE,CAAA;AACzC,EAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,KAAK,CAAA,EAAG;AAC/B,IAAA,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAAA,EAC5E;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,IAAQ,IAAA;AAC3B,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,IAAK,IAAA,CAAK,OAAO,CAAA,EAAG;AAC/C,MAAA,MAAM,IAAI,MAAM,4DAA4D,CAAA;AAAA,IAChF;AACA,IAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,IAAA,CAAK,cAAc,CAAA,EAAG;AAC7C,MAAA,MAAM,IAAI,MAAM,sEAAsE,CAAA;AAAA,IAC1F;AAAA,EACJ;AAEA,EAAA,MAAM,UAAA,GAAa;AAAA,IACf,SAAA,EAAW,MAAM,SAAA,CAAU,OAAA;AAAA,IAC3B,OAAO,KAAA,CAAM,YAAA;AAAA,IACb,MAAA;AAAA,IACA,SAAA,EAAW,MAAM,IAAA,IAAQ,CAAA;AAAA,IACzB,gBAAA,EAAkB,MAAM,cAAA,IAAkB,MAAA;AAAA,IAC1C,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,SAAA;AAAA,IACZ;AAAA,GACJ;AACA,EAAA,MAAM,EAAA,GAAK,oBAAoB,UAAU,CAAA;AAKzC,EAAA,IAAI,eAAA;AACJ,EAAA,IAAI,MAAM,aAAA,EAAe;AACrB,IAAA,IAAI,CAAC,MAAM,aAAA,CAAc,UAAA,IAAc,MAAM,aAAA,CAAc,UAAA,CAAW,WAAW,CAAA,EAAG;AAChF,MAAA,MAAM,IAAI,KAAA;AAAA,QACN;AAAA,OACJ;AAAA,IACJ;AACA,IAAA,eAAA,GAAkB,MAAM,UAAA,CAAW;AAAA,MAC/B,MAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACJ,OAAA,EAAS,MAAM,SAAA,CAAU,OAAA;AAAA,QACzB,WAAA,EAAa,MAAM,SAAA,CAAU;AAAA,OACjC;AAAA,MACA,UAAA,EAAY,MAAM,aAAA,CAAc,UAAA;AAAA,MAChC,GAAI,KAAA,CAAM,aAAA,CAAc,IAAA,KAAS,MAAA,IAAa;AAAA,QAC1C,IAAA,EAAM,MAAM,aAAA,CAAc;AAAA,OAC9B;AAAA,MACA,GAAI,KAAA,CAAM,aAAA,CAAc,iBAAA,KAAsB,MAAA,IAAa;AAAA,QACvD,SAAA,EAAW,MAAM,aAAA,CAAc;AAAA;AACnC,KACH,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,SAAA,CAAU,YAAY,EAAE,CAAA;AAErD,EAAA,MAAM,QAAA,GAA+B;AAAA,IACjC,CAAA,EAAG,gBAAA;AAAA,IACH,IAAA,EAAM,kBAAA;AAAA,IACN,EAAA;AAAA,IACA,WAAW,EAAE,OAAA,EAAS,MAAM,SAAA,CAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,IAC7D,OAAO,EAAE,OAAA,EAAS,KAAA,CAAM,YAAA,EAAc,KAAK,QAAA,EAAS;AAAA;AAAA,IAEpD,GAAI,eAAA,GAAkB,EAAE,kBAAkB,eAAA,EAAgB,GAAI,EAAE,MAAA,EAAO;AAAA,IACvE,IAAA;AAAA,IACA,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,SAAA;AAAA,IACZ,KAAA;AAAA,IACA,UAAA,EAAY;AAAA,MACR,OAAA,EAAS,KAAA,CAAM,iBAAA,IAAqB,CAAC,WAAW,CAAA;AAAA,MAChD,GAAA,EAAK,MAAM,aAAA,IAAiB;AAAA,KAChC;AAAA,IACA,GAAA,EAAK;AAAA,MACD,GAAA,EAAK,QAAA;AAAA,MACL,MAAA,EAAQ,MAAM,SAAA,CAAU,OAAA;AAAA,MACxB,KAAA,EAAO;AAAA;AACX,GACJ;AACA,EAAA,OAAO,QAAA;AACX;AAoBA,eAAsB,YAAY,KAAA,EAAkD;AAChF,EAAA,IAAI,MAAM,KAAA,CAAM,OAAA,KAAY,KAAA,CAAM,UAAA,CAAW,MAAM,OAAA,EAAS;AACxD,IAAA,MAAM,IAAI,MAAM,qEAAqE,CAAA;AAAA,EACzF;AAEA,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,KAAA,CAAM,mBAAmB,UAAA,EAAY;AACrC,IAAA,WAAA,GAAc,SAAA,GAAY,SAAA,CAAU,MAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AACzD,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,UAAA;AAAA,EAClC,CAAA,MAAO;AACH,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,SAAS,CAAA,IAAK,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,MAAA,KAAW,SAAA,CAAU,SAAS,EAAA,EAAI;AAClG,MAAA,MAAM,IAAI,MAAM,sEAAsE,CAAA;AAAA,IAC1F;AACA,IAAA,WAAA,GAAc,MAAM,OAAA,CAAQ,IAAA;AAC5B,IAAA,aAAA,GAAgB,MAAM,OAAA,CAAQ,MAAA;AAC9B,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,aAAa,CAAA,IAAK,gBAAgB,CAAA,EAAG;AACvD,MAAA,MAAM,IAAI,MAAM,4DAA4D,CAAA;AAAA,IAChF;AAAA,EACJ;AAEA,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAE3D,EAAA,MAAM,UAAA,GAAa;AAAA,IACf,OAAA,EAAS,MAAM,KAAA,CAAM,OAAA;AAAA,IACrB,YAAA,EAAc,WAAA;AAAA,IACd,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAc,KAAA,CAAM,IAAA;AAAA,IACpB,SAAA,EAAW,QAAA;AAAA,IACX,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,iBAAiB,KAAA,CAAM;AAAA,GAC3B;AACA,EAAA,MAAM,EAAA,GAAK,gBAAgB,UAAU,CAAA;AAErC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,KAAA,CAAM,YAAY,EAAE,CAAA;AAEjD,EAAA,MAAM,QAAA,GAA2B;AAAA,IAC7B,CAAA,EAAG,gBAAA;AAAA,IACH,IAAA,EAAM,cAAA;AAAA,IACN,EAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,MAAA,EAAQ,aAAA;AAAA,MACR,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,GAAA,EAAK,MAAM,GAAA,IAAO;AAAA,KACtB;AAAA,IACA,QAAQ,EAAE,OAAA,EAAS,MAAM,KAAA,CAAM,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,IACtD,SAAA,EAAW,QAAA;AAAA,IACX,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,iBAAiB,KAAA,CAAM,cAAA;AAAA,IACvB,GAAA,EAAK,MAAM,GAAA,IAAO,IAAA;AAAA,IAClB,GAAA,EAAK,EAAE,GAAA,EAAK,QAAA,EAAU,QAAQ,KAAA,CAAM,KAAA,CAAM,OAAA,EAAS,KAAA,EAAO,QAAA;AAAS,GACvE;AACA,EAAA,OAAO,QAAA;AACX;AAgBA,eAAsB,OAAO,KAAA,EAAiD;AAC1E,EAAA,MAAM,UAAU,KAAA,CAAM,UAAA,CAAW,UAAA,EAAY,OAAA,IAAW,CAAC,WAAW,CAAA;AACpE,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,IAAI,OAAA,CAAQ,SAAS,WAAW,CAAA,UAAW,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,SAAA,CAAU,OAAO,CAAA;AACjF,EAAA,IAAI,OAAA,CAAQ,SAAS,OAAO,CAAA,UAAW,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM,OAAO,CAAA;AACzE,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,OAAO,CAAA,EAAG;AACpC,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,eAAA,EAAkB,KAAA,CAAM,MAAA,CAAO,OAAO,CAAA,yCAAA;AAAA,KAC1C;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,EAAA;AAC/B,EAAA,IAAI,OAAO,MAAA,GAAS,GAAA,EAAK,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAE7E,EAAA,MAAM,WAAW,aAAA,CAAc,KAAA,CAAM,QAAA,oBAAY,IAAI,MAAM,CAAA;AAC3D,EAAA,MAAM,UAAA,GAAa;AAAA,IACf,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA;AAAA,IACtB,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,MAAA;AAAA,IACA,SAAA,EAAW;AAAA,GACf;AACA,EAAA,MAAM,EAAA,GAAK,oBAAoB,UAAU,CAAA;AAEzC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,CAAO,YAAY,EAAE,CAAA;AAElD,EAAA,MAAM,QAAA,GAA+B;AAAA,IACjC,CAAA,EAAG,gBAAA;AAAA,IACH,IAAA,EAAM,kBAAA;AAAA,IACN,EAAA;AAAA,IACA,aAAA,EAAe,MAAM,UAAA,CAAW,EAAA;AAAA,IAChC,QAAQ,EAAE,OAAA,EAAS,MAAM,MAAA,CAAO,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,IACvD,MAAA;AAAA,IACA,SAAA,EAAW,QAAA;AAAA,IACX,GAAA,EAAK,MAAM,GAAA,IAAO,IAAA;AAAA,IAClB,GAAA,EAAK,EAAE,GAAA,EAAK,QAAA,EAAU,QAAQ,KAAA,CAAM,MAAA,CAAO,OAAA,EAAS,KAAA,EAAO,QAAA;AAAS,GACxE;AACA,EAAA,OAAO,QAAA;AACX;AAuCA,SAAS,cAAc,CAAA,EAAiB;AACpC,EAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,WAAW,GAAG,CAAA;AACjD;AAEA,SAAS,UAAU,KAAA,EAAuB;AACtC,EAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,KAAK,CAAA;AAChC,EAAA,MAAM,IAAyB,UAAA,CAA8C,MAAA;AAC7E,EAAA,IAAI,CAAA,IAAK,OAAO,CAAA,CAAE,eAAA,KAAoB,UAAA,EAAY;AAC9C,IAAA,CAAA,CAAE,gBAAgB,GAAG,CAAA;AAAA,EACzB,CAAA,MAAO;AAEH,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EAC3E;AACA,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,IAAA,MAAM,CAAA,GAAI,IAAI,CAAC,CAAA;AACf,IAAA,GAAA,IAAO,EAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAAA,EACzC;AACA,EAAA,OAAO,GAAA;AACX","file":"index.mjs","sourcesContent":["// @orangecheck/agent-signer — high-level API for producing OC Agent envelopes.\n//\n// createDelegation — principal signs a scoped grant to an agent address.\n// signAsAgent — agent produces an agent-action envelope over content.\n// revoke — principal (or agent, if authorized) burns a delegation.\n//\n// Each function wraps @orangecheck/agent-core's canonical-message + id\n// computation with a caller-supplied `signMessage` wallet adapter. Nothing here\n// touches the network — OTS anchoring and Nostr publication are separate steps\n// the caller wires up.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n ENVELOPE_VERSION,\n hexEncode,\n parseScope,\n sealScopes,\n validateScope,\n} from '@orangecheck/agent-core';\nimport type {\n ScopesEncryptedEnvelope,\n ValidationOptions,\n} from '@orangecheck/agent-core';\nimport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n RevocationHolder,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared wallet-adapter shape (matches `@orangecheck/stamp-core` StampInput).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignerRef {\n address: string;\n /** Returns a base64 BIP-322 signature over the given message as UTF-8 bytes. */\n signMessage: (msg: string) => Promise<string>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createDelegation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CreateDelegationInput {\n principal: SignerRef;\n agentAddress: string;\n scopes: string[];\n bond?: DelegationBond | null;\n issuedAt?: Date;\n /** Duration from issuedAt in ms. Default 7 days. MAX 365 days per SPEC §4.4. */\n ttlMs?: number;\n /** Explicit expires_at overrides ttlMs. */\n expiresAt?: Date;\n /** 32-hex. Defaults to crypto.getRandomValues(16-byte) rendered as hex. */\n nonce?: string;\n /** Default ['principal']. */\n revocationHolders?: RevocationHolder[];\n /** Optional Nostr pointer to a revocation event. Non-cryptographic. */\n revocationRef?: string | null;\n /** Default 'strict'. 'permissive' allows unregistered scopes / constraint keys. */\n scopeMode?: ValidationOptions['mode'];\n /**\n * v1.2 private-scope (PRIVATE-SCOPE.md). When supplied, the resulting\n * delegation envelope omits the public `scopes` field and instead carries\n * `scopes_encrypted` — an OC Lock envelope wrapping the canonical scope\n * list, sealed to one or more recipient device keys. The principal signs\n * BOTH the inner OC Lock envelope (via the `signMessage` callback already\n * provided on `principal`) AND the outer OC Agent canonical message.\n *\n * Recipients MUST include at least the agent's device. Additional\n * recipients (compliance auditors, third-party verifiers) are optional.\n */\n privateScopes?: {\n /**\n * Devices authorized to read the scope set. Each is `{ address,\n * device_id, device_pk }` matching OC Lock's `DeviceRecord` shape.\n * The same `signMessage` callback on `principal` is used to sign the\n * inner OC Lock envelope.\n */\n recipients: PrivateScopeRecipient[];\n /** Optional human hint stored in the OC Lock envelope. */\n hint?: string;\n /** Optional expiry on the OC Lock envelope. Independent of the\n * delegation's expires_at; usually left unset. */\n envelopeExpiresAt?: Date | null;\n };\n}\n\n/** Mirror of @orangecheck/lock-core's DeviceRecord. */\nexport interface PrivateScopeRecipient {\n address: string;\n device_id: string;\n /** 32-byte hex X25519 public key. */\n device_pk: string;\n}\n\nexport async function createDelegation(input: CreateDelegationInput): Promise<DelegationEnvelope> {\n if (!input.scopes || input.scopes.length === 0) {\n throw new Error('createDelegation: at least one scope is required');\n }\n const mode = input.scopeMode ?? 'strict';\n for (const s of input.scopes) validateScope(parseScope(s), { mode });\n const scopes = canonicalizeScopes(input.scopes);\n\n const issuedAt = isoUtcSeconds(input.issuedAt ?? new Date());\n let expiresAtDate: Date;\n if (input.expiresAt) {\n expiresAtDate = input.expiresAt;\n } else {\n const ttl = input.ttlMs ?? 7 * 24 * 60 * 60 * 1000;\n expiresAtDate = new Date(new Date(issuedAt).getTime() + ttl);\n }\n const expiresAt = isoUtcSeconds(expiresAtDate);\n\n const durationMs = new Date(expiresAt).getTime() - new Date(issuedAt).getTime();\n if (durationMs <= 0) throw new Error('createDelegation: expires_at must be after issued_at');\n if (durationMs > 365 * 24 * 60 * 60 * 1000) {\n throw new Error('createDelegation: delegation duration must not exceed 365 days');\n }\n\n const nonce = input.nonce ?? randomHex(16);\n if (!/^[0-9a-f]{32}$/.test(nonce)) {\n throw new Error('createDelegation: nonce must be 32 lowercase hex chars');\n }\n\n const bond = input.bond ?? null;\n if (bond) {\n if (!Number.isInteger(bond.sats) || bond.sats < 0) {\n throw new Error('createDelegation: bond.sats must be a non-negative integer');\n }\n if (!/^[0-9a-f]{64}$/.test(bond.attestation_id)) {\n throw new Error('createDelegation: bond.attestation_id must be 64 lowercase hex chars');\n }\n }\n\n const canonInput = {\n principal: input.principal.address,\n agent: input.agentAddress,\n scopes,\n bond_sats: bond?.sats ?? 0,\n bond_attestation: bond?.attestation_id ?? 'none',\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n };\n const id = computeDelegationId(canonInput);\n\n // v1.2 private-scope: seal BEFORE the principal's outer signature so the\n // user's wallet sees both prompts in a deterministic order (inner Lock\n // sig first, outer OC Agent sig second).\n let scopesEncrypted: ScopesEncryptedEnvelope | undefined;\n if (input.privateScopes) {\n if (!input.privateScopes.recipients || input.privateScopes.recipients.length === 0) {\n throw new Error(\n 'createDelegation: privateScopes.recipients must contain at least the agent device'\n );\n }\n scopesEncrypted = await sealScopes({\n scopes,\n sender: {\n address: input.principal.address,\n signMessage: input.principal.signMessage,\n },\n recipients: input.privateScopes.recipients,\n ...(input.privateScopes.hint !== undefined && {\n hint: input.privateScopes.hint,\n }),\n ...(input.privateScopes.envelopeExpiresAt !== undefined && {\n expiresAt: input.privateScopes.envelopeExpiresAt,\n }),\n });\n }\n\n const sigValue = await input.principal.signMessage(id);\n\n const envelope: DelegationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-delegation',\n id,\n principal: { address: input.principal.address, alg: 'bip322' },\n agent: { address: input.agentAddress, alg: 'bip322' },\n // Public OR private mode — never both. PRIVATE-SCOPE.md §1.1.\n ...(scopesEncrypted ? { scopes_encrypted: scopesEncrypted } : { scopes }),\n bond,\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n revocation: {\n holders: input.revocationHolders ?? ['principal'],\n ref: input.revocationRef ?? null,\n },\n sig: {\n alg: 'bip322',\n pubkey: input.principal.address,\n value: sigValue,\n },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// signAsAgent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignAsAgentInput {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n /** Raw bytes to stamp, or a pre-computed { hash, length } pair. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n /** Which scope from delegation.scopes is being exercised (or a narrower sub-scope). */\n scopeExercised: string;\n ref?: string | null;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function signAsAgent(input: SignAsAgentInput): Promise<ActionEnvelope> {\n if (input.agent.address !== input.delegation.agent.address) {\n throw new Error('signAsAgent: signer address does not match delegation.agent.address');\n }\n\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n if (!input.content.hash.startsWith('sha256:') || input.content.hash.length !== 'sha256:'.length + 64) {\n throw new Error('signAsAgent: content.hash must be \"sha256:\" + 64 lowercase hex chars');\n }\n contentHash = input.content.hash;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new Error('signAsAgent: content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canonInput = {\n address: input.agent.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n };\n const id = computeActionId(canonInput);\n\n const sigValue = await input.agent.signMessage(id);\n\n const envelope: ActionEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-action',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: { address: input.agent.address, alg: 'bip322' },\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.agent.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// revoke\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevokeInput {\n signer: SignerRef;\n delegation: DelegationEnvelope;\n /** Short ASCII rationale. Defaults to empty string. */\n reason?: string;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function revoke(input: RevokeInput): Promise<RevocationEnvelope> {\n const holders = input.delegation.revocation?.holders ?? ['principal'];\n const allowed = new Set<string>();\n if (holders.includes('principal')) allowed.add(input.delegation.principal.address);\n if (holders.includes('agent')) allowed.add(input.delegation.agent.address);\n if (!allowed.has(input.signer.address)) {\n throw new Error(\n `revoke: signer ${input.signer.address} not authorized to revoke this delegation`\n );\n }\n\n const reason = input.reason ?? '';\n if (reason.length > 128) throw new Error('revoke: reason must be <=128 bytes');\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n const canonInput = {\n address: input.signer.address,\n delegation_id: input.delegation.id,\n reason,\n signed_at: signedAt,\n };\n const id = computeRevocationId(canonInput);\n\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: RevocationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-revocation',\n id,\n delegation_id: input.delegation.id,\n signer: { address: input.signer.address, alg: 'bip322' },\n reason,\n signed_at: signedAt,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.signer.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Re-exports for convenience\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n verifyAction,\n verifyActionAsOfSignedAt,\n createReplayGuard,\n DEFAULT_MAX_SKEW_MS,\n verifyDelegation,\n verifyRevocation,\n parseScope,\n isSubScope,\n canonicalizeScope,\n REGISTERED_SCOPES,\n} from '@orangecheck/agent-core';\nexport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n Scope,\n ScopeConstraint,\n AgentErrorCode,\n VerifyActionInput,\n VerifyActionAsOfSignedAtInput,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction isoUtcSeconds(d: Date): string {\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n\nfunction randomHex(bytes: number): string {\n const buf = new Uint8Array(bytes);\n const c: Crypto | undefined = (globalThis as unknown as { crypto?: Crypto }).crypto;\n if (c && typeof c.getRandomValues === 'function') {\n c.getRandomValues(buf);\n } else {\n // Non-browser fallback. Node ≥18 exposes globalThis.crypto, so this is unlikely to fire.\n for (let i = 0; i < bytes; i++) buf[i] = Math.floor(Math.random() * 256);\n }\n let out = '';\n for (let i = 0; i < bytes; i++) {\n const b = buf[i]!;\n out += b.toString(16).padStart(2, '0');\n }\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// @orangecheck/agent-signer — high-level API for producing OC Agent envelopes.\n//\n// createDelegation — principal signs a scoped grant to an agent address.\n// signAsAgent — agent produces an agent-action envelope over content.\n// revoke — principal (or agent, if authorized) burns a delegation.\n//\n// Each function wraps @orangecheck/agent-core's canonical-message + id\n// computation with a caller-supplied `signMessage` wallet adapter. Nothing here\n// touches the network — OTS anchoring and Nostr publication are separate steps\n// the caller wires up.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n ENVELOPE_VERSION,\n hexEncode,\n authoredReasonProblem,\n parseScope,\n REVOCATION_REASON_MAX_BYTES,\n sealScopes,\n validateScope,\n} from '@orangecheck/agent-core';\nimport type {\n ScopesEncryptedEnvelope,\n ValidationOptions,\n} from '@orangecheck/agent-core';\nimport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n RevocationHolder,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared wallet-adapter shape (matches `@orangecheck/stamp-core` StampInput).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignerRef {\n address: string;\n /** Returns a base64 BIP-322 signature over the given message as UTF-8 bytes. */\n signMessage: (msg: string) => Promise<string>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createDelegation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CreateDelegationInput {\n principal: SignerRef;\n agentAddress: string;\n scopes: string[];\n bond?: DelegationBond | null;\n issuedAt?: Date;\n /** Duration from issuedAt in ms. Default 7 days. MAX 365 days per SPEC §4.4. */\n ttlMs?: number;\n /** Explicit expires_at overrides ttlMs. */\n expiresAt?: Date;\n /** 32-hex. Defaults to crypto.getRandomValues(16-byte) rendered as hex. */\n nonce?: string;\n /** Default ['principal']. */\n revocationHolders?: RevocationHolder[];\n /** Optional Nostr pointer to a revocation event. Non-cryptographic. */\n revocationRef?: string | null;\n /** Default 'strict'. 'permissive' allows unregistered scopes / constraint keys. */\n scopeMode?: ValidationOptions['mode'];\n /**\n * v1.2 private-scope (PRIVATE-SCOPE.md). When supplied, the resulting\n * delegation envelope omits the public `scopes` field and instead carries\n * `scopes_encrypted` — an OC Lock envelope wrapping the canonical scope\n * list, sealed to one or more recipient device keys. The principal signs\n * BOTH the inner OC Lock envelope (via the `signMessage` callback already\n * provided on `principal`) AND the outer OC Agent canonical message.\n *\n * Recipients MUST include at least the agent's device. Additional\n * recipients (compliance auditors, third-party verifiers) are optional.\n */\n privateScopes?: {\n /**\n * Devices authorized to read the scope set. Each is `{ address,\n * device_id, device_pk }` matching OC Lock's `DeviceRecord` shape.\n * The same `signMessage` callback on `principal` is used to sign the\n * inner OC Lock envelope.\n */\n recipients: PrivateScopeRecipient[];\n /** Optional human hint stored in the OC Lock envelope. */\n hint?: string;\n /** Optional expiry on the OC Lock envelope. Independent of the\n * delegation's expires_at; usually left unset. */\n envelopeExpiresAt?: Date | null;\n };\n}\n\n/** Mirror of @orangecheck/lock-core's DeviceRecord. */\nexport interface PrivateScopeRecipient {\n address: string;\n device_id: string;\n /** 32-byte hex X25519 public key. */\n device_pk: string;\n}\n\nexport async function createDelegation(input: CreateDelegationInput): Promise<DelegationEnvelope> {\n if (!input.scopes || input.scopes.length === 0) {\n throw new Error('createDelegation: at least one scope is required');\n }\n const mode = input.scopeMode ?? 'strict';\n for (const s of input.scopes) validateScope(parseScope(s), { mode });\n const scopes = canonicalizeScopes(input.scopes);\n\n const issuedAt = isoUtcSeconds(input.issuedAt ?? new Date());\n let expiresAtDate: Date;\n if (input.expiresAt) {\n expiresAtDate = input.expiresAt;\n } else {\n const ttl = input.ttlMs ?? 7 * 24 * 60 * 60 * 1000;\n expiresAtDate = new Date(new Date(issuedAt).getTime() + ttl);\n }\n const expiresAt = isoUtcSeconds(expiresAtDate);\n\n const durationMs = new Date(expiresAt).getTime() - new Date(issuedAt).getTime();\n if (durationMs <= 0) throw new Error('createDelegation: expires_at must be after issued_at');\n if (durationMs > 365 * 24 * 60 * 60 * 1000) {\n throw new Error('createDelegation: delegation duration must not exceed 365 days');\n }\n\n const nonce = input.nonce ?? randomHex(16);\n if (!/^[0-9a-f]{32}$/.test(nonce)) {\n throw new Error('createDelegation: nonce must be 32 lowercase hex chars');\n }\n\n const bond = input.bond ?? null;\n if (bond) {\n if (!Number.isInteger(bond.sats) || bond.sats < 0) {\n throw new Error('createDelegation: bond.sats must be a non-negative integer');\n }\n if (!/^[0-9a-f]{64}$/.test(bond.attestation_id)) {\n throw new Error('createDelegation: bond.attestation_id must be 64 lowercase hex chars');\n }\n }\n\n const canonInput = {\n principal: input.principal.address,\n agent: input.agentAddress,\n scopes,\n bond_sats: bond?.sats ?? 0,\n bond_attestation: bond?.attestation_id ?? 'none',\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n };\n const id = computeDelegationId(canonInput);\n\n // v1.2 private-scope: seal BEFORE the principal's outer signature so the\n // user's wallet sees both prompts in a deterministic order (inner Lock\n // sig first, outer OC Agent sig second).\n let scopesEncrypted: ScopesEncryptedEnvelope | undefined;\n if (input.privateScopes) {\n if (!input.privateScopes.recipients || input.privateScopes.recipients.length === 0) {\n throw new Error(\n 'createDelegation: privateScopes.recipients must contain at least the agent device'\n );\n }\n scopesEncrypted = await sealScopes({\n scopes,\n sender: {\n address: input.principal.address,\n signMessage: input.principal.signMessage,\n },\n recipients: input.privateScopes.recipients,\n ...(input.privateScopes.hint !== undefined && {\n hint: input.privateScopes.hint,\n }),\n ...(input.privateScopes.envelopeExpiresAt !== undefined && {\n expiresAt: input.privateScopes.envelopeExpiresAt,\n }),\n });\n }\n\n const sigValue = await input.principal.signMessage(id);\n\n const envelope: DelegationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-delegation',\n id,\n principal: { address: input.principal.address, alg: 'bip322' },\n agent: { address: input.agentAddress, alg: 'bip322' },\n // Public OR private mode — never both. PRIVATE-SCOPE.md §1.1.\n ...(scopesEncrypted ? { scopes_encrypted: scopesEncrypted } : { scopes }),\n bond,\n issued_at: issuedAt,\n expires_at: expiresAt,\n nonce,\n revocation: {\n holders: input.revocationHolders ?? ['principal'],\n ref: input.revocationRef ?? null,\n },\n sig: {\n alg: 'bip322',\n pubkey: input.principal.address,\n value: sigValue,\n },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// signAsAgent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SignAsAgentInput {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n /** Raw bytes to stamp, or a pre-computed { hash, length } pair. */\n content: Uint8Array | { hash: string; length: number };\n mime: string;\n /** Which scope from delegation.scopes is being exercised (or a narrower sub-scope). */\n scopeExercised: string;\n ref?: string | null;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function signAsAgent(input: SignAsAgentInput): Promise<ActionEnvelope> {\n if (input.agent.address !== input.delegation.agent.address) {\n throw new Error('signAsAgent: signer address does not match delegation.agent.address');\n }\n\n let contentHash: string;\n let contentLength: number;\n if (input.content instanceof Uint8Array) {\n contentHash = 'sha256:' + hexEncode(sha256(input.content));\n contentLength = input.content.byteLength;\n } else {\n if (!input.content.hash.startsWith('sha256:') || input.content.hash.length !== 'sha256:'.length + 64) {\n throw new Error('signAsAgent: content.hash must be \"sha256:\" + 64 lowercase hex chars');\n }\n contentHash = input.content.hash;\n contentLength = input.content.length;\n if (!Number.isInteger(contentLength) || contentLength < 0) {\n throw new Error('signAsAgent: content.length must be a non-negative integer');\n }\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n\n const canonInput = {\n address: input.agent.address,\n content_hash: contentHash,\n content_length: contentLength,\n content_mime: input.mime,\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n };\n const id = computeActionId(canonInput);\n\n const sigValue = await input.agent.signMessage(id);\n\n const envelope: ActionEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-action',\n id,\n content: {\n hash: contentHash,\n length: contentLength,\n mime: input.mime,\n ref: input.ref ?? null,\n },\n signer: { address: input.agent.address, alg: 'bip322' },\n signed_at: signedAt,\n delegation_id: input.delegation.id,\n scope_exercised: input.scopeExercised,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.agent.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// revoke\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevokeInput {\n signer: SignerRef;\n delegation: DelegationEnvelope;\n /** Printable ASCII, at most 128 bytes: SPEC §9.1 allows any ASCII; we write no control character. Defaults to \"\". */\n reason?: string;\n signedAt?: Date;\n /** OTS proof / anchor data if the caller has already submitted; otherwise null. */\n ots?: ActionOts | null;\n}\n\nexport async function revoke(input: RevokeInput): Promise<RevocationEnvelope> {\n // The principal and the agent may always revoke (SPEC §9.5).\n const { principal, agent } = input.delegation;\n if (input.signer.address !== principal.address && input.signer.address !== agent.address) {\n throw new Error(\n `revoke: signer ${input.signer.address} not authorized to revoke this delegation`\n );\n }\n\n const reason = input.reason ?? '';\n // SPEC §9.1: a signer writes printable ASCII of at most 128 bytes.\n switch (authoredReasonProblem(reason)) {\n case null:\n break;\n case 'too_long':\n throw new Error(`revoke: reason must be at most ${REVOCATION_REASON_MAX_BYTES} bytes`);\n case 'not_ascii':\n throw new Error('revoke: reason must be ASCII');\n case 'control_character':\n throw new Error('revoke: reason must have no line break or other control character');\n case 'not_text':\n throw new Error('revoke: reason must be text');\n }\n\n const signedAt = isoUtcSeconds(input.signedAt ?? new Date());\n const canonInput = {\n address: input.signer.address,\n delegation_id: input.delegation.id,\n reason,\n signed_at: signedAt,\n };\n const id = computeRevocationId(canonInput);\n\n const sigValue = await input.signer.signMessage(id);\n\n const envelope: RevocationEnvelope = {\n v: ENVELOPE_VERSION,\n kind: 'agent-revocation',\n id,\n delegation_id: input.delegation.id,\n signer: { address: input.signer.address, alg: 'bip322' },\n reason,\n signed_at: signedAt,\n ots: input.ots ?? null,\n sig: { alg: 'bip322', pubkey: input.signer.address, value: sigValue },\n };\n return envelope;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Re-exports for convenience\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n canonicalizeScopes,\n computeActionId,\n computeDelegationId,\n computeRevocationId,\n verifyAction,\n verifyActionAsOfSignedAt,\n createReplayGuard,\n DEFAULT_MAX_SKEW_MS,\n verifyDelegation,\n verifyRevocation,\n parseScope,\n isSubScope,\n canonicalizeScope,\n REGISTERED_SCOPES,\n authoredReasonProblem,\n REVOCATION_REASON_MAX_BYTES,\n shownReason,\n} from '@orangecheck/agent-core';\nexport type {\n ActionEnvelope,\n ActionOts,\n DelegationBond,\n DelegationEnvelope,\n RevocationEnvelope,\n Scope,\n ScopeConstraint,\n AgentErrorCode,\n VerifyActionInput,\n VerifyActionAsOfSignedAtInput,\n} from '@orangecheck/agent-core';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction isoUtcSeconds(d: Date): string {\n return d.toISOString().replace(/\\.\\d+Z$/, 'Z');\n}\n\nfunction randomHex(bytes: number): string {\n const buf = new Uint8Array(bytes);\n const c: Crypto | undefined = (globalThis as unknown as { crypto?: Crypto }).crypto;\n if (c && typeof c.getRandomValues === 'function') {\n c.getRandomValues(buf);\n } else {\n // Non-browser fallback. Node ≥18 exposes globalThis.crypto, so this is unlikely to fire.\n for (let i = 0; i < bytes; i++) buf[i] = Math.floor(Math.random() * 256);\n }\n let out = '';\n for (let i = 0; i < bytes; i++) {\n const b = buf[i]!;\n out += b.toString(16).padStart(2, '0');\n }\n return out;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangecheck/agent-signer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "OC Agent signer: createDelegation(), signAsAgent(), revoke(). Composes @orangecheck/agent-core with a wallet-adapter signing function and optional OTS anchoring.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@noble/hashes": "^1.5.0",
|
|
52
|
-
"@orangecheck/agent-core": "^2.
|
|
52
|
+
"@orangecheck/agent-core": "^2.9.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/node": "^22.10.2",
|
package/src/index.test.ts
CHANGED
|
@@ -160,37 +160,66 @@ describe('revoke', () => {
|
|
|
160
160
|
expect(r.ok).toBe(true);
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
it('
|
|
163
|
+
it('refuses a reason the spec refuses, before it asks for a signature', async () => {
|
|
164
164
|
const delegation = await createDelegation({
|
|
165
165
|
principal: { address: PRINCIPAL, signMessage: fakeSign(PRINCIPAL) },
|
|
166
166
|
agentAddress: AGENT,
|
|
167
167
|
scopes: ['stamp:sign(mime=text/markdown)'],
|
|
168
168
|
ttlMs: 60_000,
|
|
169
169
|
});
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
let asked = 0;
|
|
171
|
+
const signer = {
|
|
172
|
+
address: PRINCIPAL,
|
|
173
|
+
signMessage: async (m: string) => {
|
|
174
|
+
asked++;
|
|
175
|
+
return fakeSign(PRINCIPAL)(m);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
for (const [reason, why] of [
|
|
179
|
+
['x'.repeat(129), /at most 128 bytes/],
|
|
180
|
+
['\u00e9'.repeat(128), /must be ASCII/],
|
|
181
|
+
['key lost\nsigned_at: 2020-01-01T00:00:00Z', /control character/],
|
|
182
|
+
['tab\there', /control character/],
|
|
183
|
+
] as const) {
|
|
184
|
+
await expect(revoke({ signer, delegation, reason })).rejects.toThrow(why);
|
|
185
|
+
}
|
|
186
|
+
expect(asked).toBe(0);
|
|
187
|
+
const rev = await revoke({ signer, delegation, reason: 'x'.repeat(128) });
|
|
188
|
+
expect(rev.reason).toHaveLength(128);
|
|
189
|
+
expect(asked).toBe(1);
|
|
176
190
|
});
|
|
177
191
|
|
|
178
|
-
it('
|
|
192
|
+
it('rejects a revocation from a signer who is neither principal nor agent', async () => {
|
|
193
|
+
const OTHER = 'bc1qother0000000000000000000000000000000000';
|
|
179
194
|
const delegation = await createDelegation({
|
|
180
195
|
principal: { address: PRINCIPAL, signMessage: fakeSign(PRINCIPAL) },
|
|
181
196
|
agentAddress: AGENT,
|
|
182
197
|
scopes: ['stamp:sign(mime=text/markdown)'],
|
|
183
198
|
ttlMs: 60_000,
|
|
184
|
-
revocationHolders: ['principal', 'agent'],
|
|
185
|
-
});
|
|
186
|
-
const rev = await revoke({
|
|
187
|
-
signer: { address: AGENT, signMessage: fakeSign(AGENT) },
|
|
188
|
-
delegation,
|
|
189
|
-
reason: 'self-revoke',
|
|
190
199
|
});
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
await expect(
|
|
201
|
+
revoke({ signer: { address: OTHER, signMessage: fakeSign(OTHER) }, delegation })
|
|
202
|
+
).rejects.toThrow(/not authorized/);
|
|
193
203
|
});
|
|
204
|
+
|
|
205
|
+
for (const revocationHolders of [undefined, ['principal'] as const, ['principal', 'agent'] as const]) {
|
|
206
|
+
it(`lets the agent revoke whatever holders says (${JSON.stringify(revocationHolders)})`, async () => {
|
|
207
|
+
const delegation = await createDelegation({
|
|
208
|
+
principal: { address: PRINCIPAL, signMessage: fakeSign(PRINCIPAL) },
|
|
209
|
+
agentAddress: AGENT,
|
|
210
|
+
scopes: ['stamp:sign(mime=text/markdown)'],
|
|
211
|
+
ttlMs: 60_000,
|
|
212
|
+
...(revocationHolders ? { revocationHolders: [...revocationHolders] } : {}),
|
|
213
|
+
});
|
|
214
|
+
const rev = await revoke({
|
|
215
|
+
signer: { address: AGENT, signMessage: fakeSign(AGENT) },
|
|
216
|
+
delegation,
|
|
217
|
+
reason: 'self-revoke',
|
|
218
|
+
});
|
|
219
|
+
const r = await verifyRevocation({ envelope: rev, delegation, verifyBip322: fakeVerify });
|
|
220
|
+
expect(r.ok).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
194
223
|
});
|
|
195
224
|
|
|
196
225
|
describe('re-exported verifiers', () => {
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
computeRevocationId,
|
|
18
18
|
ENVELOPE_VERSION,
|
|
19
19
|
hexEncode,
|
|
20
|
+
authoredReasonProblem,
|
|
20
21
|
parseScope,
|
|
22
|
+
REVOCATION_REASON_MAX_BYTES,
|
|
21
23
|
sealScopes,
|
|
22
24
|
validateScope,
|
|
23
25
|
} from '@orangecheck/agent-core';
|
|
@@ -286,7 +288,7 @@ export async function signAsAgent(input: SignAsAgentInput): Promise<ActionEnvelo
|
|
|
286
288
|
export interface RevokeInput {
|
|
287
289
|
signer: SignerRef;
|
|
288
290
|
delegation: DelegationEnvelope;
|
|
289
|
-
/**
|
|
291
|
+
/** Printable ASCII, at most 128 bytes: SPEC §9.1 allows any ASCII; we write no control character. Defaults to "". */
|
|
290
292
|
reason?: string;
|
|
291
293
|
signedAt?: Date;
|
|
292
294
|
/** OTS proof / anchor data if the caller has already submitted; otherwise null. */
|
|
@@ -294,18 +296,28 @@ export interface RevokeInput {
|
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
export async function revoke(input: RevokeInput): Promise<RevocationEnvelope> {
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
300
|
-
if (holders.includes('agent')) allowed.add(input.delegation.agent.address);
|
|
301
|
-
if (!allowed.has(input.signer.address)) {
|
|
299
|
+
// The principal and the agent may always revoke (SPEC §9.5).
|
|
300
|
+
const { principal, agent } = input.delegation;
|
|
301
|
+
if (input.signer.address !== principal.address && input.signer.address !== agent.address) {
|
|
302
302
|
throw new Error(
|
|
303
303
|
`revoke: signer ${input.signer.address} not authorized to revoke this delegation`
|
|
304
304
|
);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
const reason = input.reason ?? '';
|
|
308
|
-
|
|
308
|
+
// SPEC §9.1: a signer writes printable ASCII of at most 128 bytes.
|
|
309
|
+
switch (authoredReasonProblem(reason)) {
|
|
310
|
+
case null:
|
|
311
|
+
break;
|
|
312
|
+
case 'too_long':
|
|
313
|
+
throw new Error(`revoke: reason must be at most ${REVOCATION_REASON_MAX_BYTES} bytes`);
|
|
314
|
+
case 'not_ascii':
|
|
315
|
+
throw new Error('revoke: reason must be ASCII');
|
|
316
|
+
case 'control_character':
|
|
317
|
+
throw new Error('revoke: reason must have no line break or other control character');
|
|
318
|
+
case 'not_text':
|
|
319
|
+
throw new Error('revoke: reason must be text');
|
|
320
|
+
}
|
|
309
321
|
|
|
310
322
|
const signedAt = isoUtcSeconds(input.signedAt ?? new Date());
|
|
311
323
|
const canonInput = {
|
|
@@ -351,6 +363,9 @@ export {
|
|
|
351
363
|
isSubScope,
|
|
352
364
|
canonicalizeScope,
|
|
353
365
|
REGISTERED_SCOPES,
|
|
366
|
+
authoredReasonProblem,
|
|
367
|
+
REVOCATION_REASON_MAX_BYTES,
|
|
368
|
+
shownReason,
|
|
354
369
|
} from '@orangecheck/agent-core';
|
|
355
370
|
export type {
|
|
356
371
|
ActionEnvelope,
|