@metamynd/agentsafe-signer 0.20.0 → 0.20.3
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/daemon.mjs +5 -6
- package/package.json +1 -1
- package/policy-core.mjs +57 -0
package/daemon.mjs
CHANGED
|
@@ -84,10 +84,10 @@ export function buildServiceCallMessage({ action, authorizationId, fields, nonce
|
|
|
84
84
|
export const DEFAULT_CHECKPOINT_INTERVAL_MS = 15 * 60 * 1000;
|
|
85
85
|
|
|
86
86
|
// Matches agentsafe-guard.mjs's own REPORTABLE_LOCAL_DECISIONS exactly (LOCAL_DECISIONS the
|
|
87
|
-
// backend's /policy/decisions/local
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
const REPORTABLE_LOCAL_DECISIONS = new Set(['allow', 'observe', 'block', 'escalate']);
|
|
87
|
+
// backend's /policy/decisions/local accepts) — this daemon never signs another value here just because a caller asked;
|
|
88
|
+
// only a genuine local-first verdict shape, or (since 0.20.3, N-4) a containment refusal the guard gave from the
|
|
89
|
+
// server's own `contained` flag, is a valid thing to have signed under this op. Never 'decommission'.
|
|
90
|
+
const REPORTABLE_LOCAL_DECISIONS = new Set(['allow', 'observe', 'block', 'escalate', 'suspend', 'quarantine']);
|
|
91
91
|
|
|
92
92
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
93
93
|
const HEX_RE = /^[0-9a-f]+$/i;
|
|
@@ -390,8 +390,7 @@ export class SignerDaemon {
|
|
|
390
390
|
|
|
391
391
|
/**
|
|
392
392
|
* An agent settling its OWN hold that nobody has claimed (MAGP §8.7.4): capture it, or void it. The issuer accepts an
|
|
393
|
-
* unclaimed settlement only from that hold's agent
|
|
394
|
-
* anyone. Built from validated fields under the fixed MAGP-SETTLE-v1 prefix, which no other message this daemon signs
|
|
393
|
+
* unclaimed settlement only from that hold's agent or a counterparty the owner registered, on every owner. Built from validated fields under the fixed MAGP-SETTLE-v1 prefix, which no other message this daemon signs
|
|
395
394
|
* starts with; it names the agent, so it is never a counterparty's signature (sign-service-call, MAGP-SERVICE-v1), and an
|
|
396
395
|
* agent cannot use it to claim. Agent-role only.
|
|
397
396
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamynd/agentsafe-signer",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.3",
|
|
4
4
|
"description": "Local signer daemon for AgentSafe agent/service keys \u2014 the key never enters the calling guard's own process. See docs/design/agent-key-custody-local-signer-daemon-plan.md.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./daemon.mjs",
|
package/policy-core.mjs
CHANGED
|
@@ -43,6 +43,57 @@ function riskFloorFor(mandate, target) {
|
|
|
43
43
|
}
|
|
44
44
|
return floor;
|
|
45
45
|
}
|
|
46
|
+
var DEFAULT_AMOUNT_SHARE_HIGH = 0.7;
|
|
47
|
+
function grantsFor(mandate, target) {
|
|
48
|
+
return (mandate?.permission ?? []).filter((p) => !!p && typeof p === "object" && (p.target ?? mandate?.target) === target);
|
|
49
|
+
}
|
|
50
|
+
function perTxnCapFor(mandate, target) {
|
|
51
|
+
let cap = null;
|
|
52
|
+
for (const p of grantsFor(mandate, target)) {
|
|
53
|
+
for (const c of p.constraint ?? []) {
|
|
54
|
+
if (c?.leftOperand !== "mm:payAmount") continue;
|
|
55
|
+
const n = Number(c.rightOperand);
|
|
56
|
+
if (Number.isFinite(n) && n > 0 && (cap === null || n < cap)) cap = n;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return cap;
|
|
60
|
+
}
|
|
61
|
+
function hasMerchantAllowList(mandate, target) {
|
|
62
|
+
return grantsFor(mandate, target).some((p) => (p.constraint ?? []).some((c) => c?.leftOperand === "mm:merchant"));
|
|
63
|
+
}
|
|
64
|
+
function riskSignalSettings(mandate, target) {
|
|
65
|
+
const grants = grantsFor(mandate, target);
|
|
66
|
+
if (grants.length === 0) return { amountShare: DEFAULT_AMOUNT_SHARE_HIGH, newMerchant: true };
|
|
67
|
+
let amountShare = null;
|
|
68
|
+
let newMerchant = false;
|
|
69
|
+
for (const p of grants) {
|
|
70
|
+
const s = p.riskSignals;
|
|
71
|
+
if (s === false) continue;
|
|
72
|
+
const cfg = s && typeof s === "object" ? s : {};
|
|
73
|
+
const share = cfg.amountShare === false ? null : cfg.amountShare === void 0 ? DEFAULT_AMOUNT_SHARE_HIGH : Number(cfg.amountShare);
|
|
74
|
+
if (share !== null && Number.isFinite(share) && share > 0 && share <= 1 && (amountShare === null || share < amountShare)) amountShare = share;
|
|
75
|
+
if (cfg.newMerchant !== false) newMerchant = true;
|
|
76
|
+
}
|
|
77
|
+
return { amountShare, newMerchant };
|
|
78
|
+
}
|
|
79
|
+
function riskSignalsFor(mandate, target, amount, opts = {}) {
|
|
80
|
+
const out = [];
|
|
81
|
+
const tier = riskFloorFor(mandate, target);
|
|
82
|
+
if (tier) out.push({ signal: "owner-tier", level: tier, detail: `the owner's risk tier for ${target}` });
|
|
83
|
+
const settings = riskSignalSettings(mandate, target);
|
|
84
|
+
const n = typeof amount === "number" ? amount : Number(amount);
|
|
85
|
+
const cap = perTxnCapFor(mandate, target);
|
|
86
|
+
if (settings.amountShare !== null && cap !== null && Number.isFinite(n) && n > 0 && n >= settings.amountShare * cap) {
|
|
87
|
+
out.push({ signal: "amount-share", level: "high", detail: `${Math.round(n / cap * 100)}% of the ${cap} per-transaction cap (review from ${Math.round(settings.amountShare * 100)}%)` });
|
|
88
|
+
}
|
|
89
|
+
if (opts.newMerchant === true && settings.newMerchant && !hasMerchantAllowList(mandate, target)) {
|
|
90
|
+
out.push({ signal: "new-merchant", level: "high", detail: "the first payment to this merchant" });
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function effectiveRiskFloor(mandate, target, amount, opts = {}) {
|
|
95
|
+
return maxRisk(...riskSignalsFor(mandate, target, amount, opts).map((s) => s.level));
|
|
96
|
+
}
|
|
46
97
|
function requiresPayloadBindingFor(mandate, target) {
|
|
47
98
|
if (!mandate) return false;
|
|
48
99
|
for (const p of mandate.permission ?? []) {
|
|
@@ -793,6 +844,7 @@ export {
|
|
|
793
844
|
AUTH_MESSAGE_V2_TAG,
|
|
794
845
|
CATALOGUED_ATOMS,
|
|
795
846
|
CONTEXT_UNVERIFIABLE,
|
|
847
|
+
DEFAULT_AMOUNT_SHARE_HIGH,
|
|
796
848
|
JURISDICTION_ATOM,
|
|
797
849
|
MODES_BY_RANK,
|
|
798
850
|
MODE_RANK,
|
|
@@ -815,10 +867,12 @@ export {
|
|
|
815
867
|
canAuthorize,
|
|
816
868
|
contextFieldProblem,
|
|
817
869
|
documentEnforcesJurisdiction,
|
|
870
|
+
effectiveRiskFloor,
|
|
818
871
|
evaluate,
|
|
819
872
|
evaluateBoundStandards,
|
|
820
873
|
evaluateMandate,
|
|
821
874
|
evaluateStandardRules,
|
|
875
|
+
hasMerchantAllowList,
|
|
822
876
|
isAuthorityFailure,
|
|
823
877
|
isOperatingMode,
|
|
824
878
|
isProvenance,
|
|
@@ -829,6 +883,7 @@ export {
|
|
|
829
883
|
moreRestrictive,
|
|
830
884
|
normalizeRiskLevel,
|
|
831
885
|
operatingModeGate,
|
|
886
|
+
perTxnCapFor,
|
|
832
887
|
provenanceOf,
|
|
833
888
|
releaseHold,
|
|
834
889
|
remainingBudget,
|
|
@@ -836,6 +891,8 @@ export {
|
|
|
836
891
|
requiredContextOf,
|
|
837
892
|
requiresPayloadBindingFor,
|
|
838
893
|
riskFloorFor,
|
|
894
|
+
riskSignalSettings,
|
|
895
|
+
riskSignalsFor,
|
|
839
896
|
sumEventField,
|
|
840
897
|
validateMolecules
|
|
841
898
|
};
|