@reinconsole/graph 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // ../../packages/core/dist/chunk-AW74KJ6X.js
1
+ // ../../packages/core/dist/chunk-SRKPESD5.js
2
2
  import { z } from "zod";
3
3
  var ULID_BODY = "[0-9A-HJKMNP-TV-Z]{26}";
4
4
  function prefixedId(prefix) {
@@ -14,7 +14,7 @@ var SessionId = prefixedId("ses");
14
14
  var GateReceiptId = prefixedId("grc");
15
15
  var ApiKeyId = prefixedId("key");
16
16
  var ApproverKeyId = prefixedId("apk");
17
- var ApiKeyScope = z.enum(["read", "evaluate", "approve", "report", "admin"]);
17
+ var ApiKeyScope = z.enum(["read", "evaluate", "approve", "report", "identity", "admin"]);
18
18
  var ApiKey = z.object({
19
19
  id: ApiKeyId,
20
20
  name: z.string().min(1).max(200),
@@ -1197,6 +1197,13 @@ var AuthError = class _AuthError extends Error {
1197
1197
  var ApiKeyAuth = class {
1198
1198
  store;
1199
1199
  now;
1200
+ /**
1201
+ * Keys with an authority write (rotate, revoke, lift) in flight. A usage
1202
+ * touch writes back a WHOLE record snapshot, so one taken while such a write
1203
+ * is on its way to disk would land after it and undo it -- re-open a revoked
1204
+ * key, or give a claimed sandbox its expiry back. Touches skip these keys.
1205
+ */
1206
+ mutating = /* @__PURE__ */ new Set();
1200
1207
  constructor(options = {}) {
1201
1208
  this.store = options.store ?? new InMemoryApiKeyStore();
1202
1209
  this.now = options.now ?? Date.now;
@@ -1250,16 +1257,31 @@ var ApiKeyAuth = class {
1250
1257
  rotated.previousSecretHash = record.secretHash;
1251
1258
  rotated.previousSecretExpiresAt = new Date(this.now() + graceMs);
1252
1259
  }
1253
- await this.store.put(rotated);
1260
+ await this.write(rotated);
1254
1261
  return { key: toPublicApiKey(rotated), secret };
1255
1262
  }
1256
1263
  async revoke(keyId) {
1257
1264
  const record = this.store.get(keyId);
1258
1265
  if (!record) return void 0;
1259
1266
  const revoked = { ...record, revokedAt: new Date(this.now()) };
1260
- await this.store.put(revoked);
1267
+ await this.write(revoked);
1261
1268
  return toPublicApiKey(revoked);
1262
1269
  }
1270
+ /**
1271
+ * Make an expiring key permanent -- what claiming a sandbox is (Sprint 13).
1272
+ * Revoked keys stay revoked: lifting an expiry never resurrects anything.
1273
+ * Answers undefined for an unknown key, the record unchanged when there was
1274
+ * no expiry to lift.
1275
+ */
1276
+ async clearExpiry(keyId) {
1277
+ const record = this.store.get(keyId);
1278
+ if (!record) return void 0;
1279
+ if (record.expiresAt === void 0) return toPublicApiKey(record);
1280
+ const lifted = { ...record };
1281
+ delete lifted.expiresAt;
1282
+ await this.write(lifted);
1283
+ return toPublicApiKey(lifted);
1284
+ }
1263
1285
  list() {
1264
1286
  return this.store.list().map(toPublicApiKey);
1265
1287
  }
@@ -1301,9 +1323,19 @@ var ApiKeyAuth = class {
1301
1323
  void this.touch(record);
1302
1324
  return toPublicApiKey(record);
1303
1325
  }
1326
+ /** An authority write that usage touches must not overtake (see `mutating`). */
1327
+ async write(record) {
1328
+ this.mutating.add(record.id);
1329
+ try {
1330
+ await this.store.put(record);
1331
+ } finally {
1332
+ this.mutating.delete(record.id);
1333
+ }
1334
+ }
1304
1335
  /** Throttled write-through of `lastUsedAt` — never on the critical path. */
1305
1336
  async touch(record) {
1306
1337
  const at = this.now();
1338
+ if (this.mutating.has(record.id)) return;
1307
1339
  if (record.lastUsedAt && at - record.lastUsedAt.getTime() < TOUCH_INTERVAL_MS) return;
1308
1340
  try {
1309
1341
  await this.store.put({ ...record, lastUsedAt: new Date(at) });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../packages/core/src/ids.ts","../../../packages/core/src/api-key.ts","../../../packages/core/src/ulid.ts","../../../packages/core/src/chain.ts","../../../packages/core/src/money.ts","../../../packages/core/src/glob.ts","../../../packages/core/src/agent.ts","../../../packages/core/src/erc8004.ts","../../../packages/core/src/intent.ts","../../../packages/core/src/decision.ts","../../../packages/core/src/canonical.ts","../../../packages/core/src/session.ts","../../../packages/core/src/payment.ts","../../../packages/core/src/receipt.ts","../../../packages/core/src/gate-receipt.ts","../../../packages/core/src/policy.ts","../../../packages/core/src/liveness.ts","../../../packages/core/src/approval.ts","../../../packages/core/src/reputation.ts","../../../packages/core/src/events.ts","../src/evidence.ts","../src/intents.ts","../src/scoring.ts","../src/graph.ts","../src/server.ts","../../../packages/core/src/auth.ts"],"sourcesContent":["import { z } from 'zod';\n\n/** Crockford base32 ULID body (26 chars), as produced by `ulid()`. */\nconst ULID_BODY = '[0-9A-HJKMNP-TV-Z]{26}';\n\n/**\n * A zod schema for a Stripe-style prefixed ULID, e.g. `agt_01J7...`.\n * Centralizing this keeps id formats consistent across DB rows, API payloads,\n * and SDK types.\n */\nexport function prefixedId(prefix: string) {\n return z\n .string()\n .regex(new RegExp(`^${prefix}_${ULID_BODY}$`), `expected a \"${prefix}_\" prefixed id`);\n}\n\nexport const OrgId = prefixedId('org');\nexport const AgentId = prefixedId('agt');\nexport const PolicyId = prefixedId('pol');\nexport const IntentId = prefixedId('int');\nexport const DecisionId = prefixedId('dec');\nexport const ReceiptId = prefixedId('rcp');\nexport const SessionId = prefixedId('ses');\nexport const GateReceiptId = prefixedId('grc');\nexport const ApiKeyId = prefixedId('key');\nexport const ApproverKeyId = prefixedId('apk');\n\nexport type OrgId = z.infer<typeof OrgId>;\nexport type AgentId = z.infer<typeof AgentId>;\nexport type PolicyId = z.infer<typeof PolicyId>;\nexport type IntentId = z.infer<typeof IntentId>;\nexport type DecisionId = z.infer<typeof DecisionId>;\nexport type ReceiptId = z.infer<typeof ReceiptId>;\nexport type SessionId = z.infer<typeof SessionId>;\nexport type GateReceiptId = z.infer<typeof GateReceiptId>;\nexport type ApiKeyId = z.infer<typeof ApiKeyId>;\nexport type ApproverKeyId = z.infer<typeof ApproverKeyId>;\n","import { z } from 'zod';\nimport { AgentId, ApiKeyId, OrgId } from './ids.js';\n\n/**\n * What a key is allowed to reach. `admin` satisfies every requirement (it is\n * the issuing/rotating key); the rest are least-privilege grants so an agent\n * runtime carrying an `evaluate` key cannot rewrite policy, and an approval\n * relay cannot read the spend history.\n *\n * `report` is the graph's ingestion grant, and it is deliberately NOT folded\n * into `evaluate`: evidence written to the reputation graph is about OTHER\n * subjects, so a fleet key that may spend its own budget has no business\n * moving a vendor's score. The engine's own reporting routes ride `evaluate`\n * because there the reporter IS the spender — that reasoning does not reach\n * across to a shared graph.\n */\nexport const ApiKeyScope = z.enum(['read', 'evaluate', 'approve', 'report', 'admin']);\nexport type ApiKeyScope = z.infer<typeof ApiKeyScope>;\n\n/**\n * The public view of an API key — everything except the secret. This is what\n * the API returns on list/issue; the secret itself is shown exactly once, at\n * issuance, and never persisted in recoverable form (see {@link ApiKeyRecord}).\n */\nexport const ApiKey = z.object({\n id: ApiKeyId,\n name: z.string().min(1).max(200),\n scopes: z.array(ApiKeyScope).min(1),\n /**\n * The org this key acts inside. ABSENT means unscoped — an operator key that\n * reads and writes across every org, which is what a self-hosted single-org\n * deployment and every env-seeded boot secret have always been. That is the\n * compatibility rule the whole tenancy design rests on: adding tenancy\n * changed no existing key's behaviour, because no existing key has this\n * field.\n *\n * Present, it is a hard confinement, not a default: the key cannot read,\n * spend, approve or govern outside the org, and a write it submits is\n * stamped with this org rather than the one the body asked for.\n */\n orgId: OrgId.optional(),\n /**\n * Narrow an org-scoped key further, to specific agents — what an agent\n * runtime should carry, so a stolen key spends one agent's budget rather\n * than the org's. Only meaningful alongside {@link ApiKey.orgId}: agent\n * narrowing without an org is refused at issuance, because the scope it\n * would describe has no org to resolve policies and approvers in.\n *\n * Capped at 64: a list long enough to need more is a fleet, and a fleet is\n * what the org scope is for.\n */\n agentIds: z.array(AgentId).max(64).optional(),\n createdAt: z.coerce.date(),\n /** Last successful authentication, for spotting keys that can be retired. */\n lastUsedAt: z.coerce.date().optional(),\n /** Set by revoke(); a revoked key never authenticates again. */\n revokedAt: z.coerce.date().optional(),\n /** Set by rotate(); with `previousSecretExpiresAt` it dates the overlap. */\n rotatedAt: z.coerce.date().optional(),\n /**\n * Past this instant the key stops authenticating, like a revoked one.\n * ABSENT means it never expires -- every key before the sandbox, and every\n * key an operator mints. Only the anonymous sandbox sets it, and a key minted\n * BY an expiring key inherits the deadline: a sandbox that could mint itself\n * a permanent key would not be a 7-day sandbox.\n */\n expiresAt: z.coerce.date().optional(),\n});\nexport type ApiKey = z.infer<typeof ApiKey>;\n\n/**\n * SERVER-SIDE ONLY. The stored form: the public record plus sha256 digests of\n * the secrets. Digests, not secrets — a stolen database yields nothing that\n * authenticates. Rotation keeps the outgoing digest alive until\n * `previousSecretExpiresAt` so a running fleet can roll over without a\n * flag-day restart; past that instant only the current secret verifies.\n *\n * This shape never leaves the engine: every route answers with {@link ApiKey}.\n */\nexport const ApiKeyRecord = ApiKey.extend({\n secretHash: z.string(),\n previousSecretHash: z.string().optional(),\n previousSecretExpiresAt: z.coerce.date().optional(),\n});\nexport type ApiKeyRecord = z.infer<typeof ApiKeyRecord>;\n\n/** Strip the digests: the only sanctioned way to put a key on the wire. */\nexport function toPublicApiKey(record: ApiKeyRecord): ApiKey {\n const { secretHash, previousSecretHash, previousSecretExpiresAt, ...pub } = record;\n void secretHash;\n void previousSecretHash;\n void previousSecretExpiresAt;\n return pub;\n}\n","/**\n * Minimal ULID generation, dependency-free. ULIDs are lexicographically\n * sortable (time-prefixed) and url-safe, which is why Rein uses them for all\n * primary keys instead of UUIDs. Works in Node 22 and modern browsers/edge via\n * the WebCrypto `globalThis.crypto`.\n */\n\n// Crockford's base32 alphabet (excludes I, L, O, U to avoid ambiguity).\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\n\nfunction randomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n\nfunction encodeTime(now: number): string {\n let time = now;\n let out = '';\n for (let i = TIME_LEN - 1; i >= 0; i--) {\n out = ENCODING.charAt(time % 32) + out;\n time = Math.floor(time / 32);\n }\n return out;\n}\n\nfunction encodeRandom(): string {\n let out = '';\n for (const byte of randomBytes(RANDOM_LEN)) {\n out += ENCODING.charAt(byte % 32);\n }\n return out;\n}\n\n/** Generate a 26-character ULID. */\nexport function ulid(seedTime: number = Date.now()): string {\n return encodeTime(seedTime) + encodeRandom();\n}\n\n/** Generate a Stripe-style prefixed id, e.g. `newId('agt')` -> `agt_01J...`. */\nexport function newId<P extends string>(prefix: P): `${P}_${string}` {\n return `${prefix}_${ulid()}`;\n}\n","import { z } from 'zod';\n\n/**\n * Chains Rein observes/governs. x402 is multi-chain; Base and Solana ship\n * first, Polygon and BNB follow. The policy/ledger domain is rail-agnostic —\n * this enum is the only place chains are enumerated.\n */\nexport const Chain = z.enum(['base', 'solana', 'polygon', 'bnb']);\nexport type Chain = z.infer<typeof Chain>;\n\n/** Stablecoins settled over x402. */\nexport const Asset = z.enum(['USDC', 'USDT', 'EURC']);\nexport type Asset = z.infer<typeof Asset>;\n","import { z } from 'zod';\n\n/**\n * Monetary amounts in Rein are always non-negative **decimal strings**, never\n * floats. Float arithmetic silently loses precision on values like 0.1 + 0.2,\n * which is unacceptable for money. All comparison/aggregation goes through the\n * BigInt-backed helpers below.\n */\nexport const DecimalString = z\n .string()\n .regex(/^\\d+(\\.\\d+)?$/, 'must be a non-negative decimal string, e.g. \"5.00\"');\nexport type DecimalString = z.infer<typeof DecimalString>;\n\nconst DECIMAL_RE = /^\\d+(\\.\\d+)?$/;\n\nexport function isValidDecimal(value: string): boolean {\n return DECIMAL_RE.test(value);\n}\n\nfunction assertDecimal(value: string): void {\n if (!isValidDecimal(value)) {\n throw new TypeError(`invalid decimal string: ${JSON.stringify(value)}`);\n }\n}\n\nfunction fractionLength(value: string): number {\n const dot = value.indexOf('.');\n return dot === -1 ? 0 : value.length - dot - 1;\n}\n\n/** Scale a decimal string into an integer BigInt at a fixed number of fraction digits. */\nfunction toScaled(value: string, scale: number): bigint {\n const dot = value.indexOf('.');\n const intPart = dot === -1 ? value : value.slice(0, dot);\n const fracPart = dot === -1 ? '' : value.slice(dot + 1);\n const paddedFrac = (fracPart + '0'.repeat(scale)).slice(0, scale);\n return BigInt(intPart + paddedFrac);\n}\n\n/** Render a scaled BigInt back to a normalized decimal string (no trailing zeros). */\nfunction fromScaled(scaled: bigint, scale: number): string {\n if (scale === 0) return scaled.toString();\n const digits = scaled.toString().padStart(scale + 1, '0');\n const intPart = digits.slice(0, digits.length - scale);\n const fracPart = digits.slice(digits.length - scale).replace(/0+$/, '');\n return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;\n}\n\n/** Compare two decimal strings. Returns -1 (a<b), 0 (a==b), or 1 (a>b). */\nexport function compareDecimal(a: string, b: string): -1 | 0 | 1 {\n assertDecimal(a);\n assertDecimal(b);\n const scale = Math.max(fractionLength(a), fractionLength(b));\n const av = toScaled(a, scale);\n const bv = toScaled(b, scale);\n return av < bv ? -1 : av > bv ? 1 : 0;\n}\n\n/** Sum a list of decimal strings without floating-point error. */\nexport function sumDecimal(values: readonly string[]): string {\n if (values.length === 0) return '0';\n let scale = 0;\n for (const v of values) {\n assertDecimal(v);\n scale = Math.max(scale, fractionLength(v));\n }\n let total = 0n;\n for (const v of values) total += toScaled(v, scale);\n return fromScaled(total, scale);\n}\n\n/**\n * `a - b` for decimal strings, exact. Requires `a >= b`: the one caller is\n * \"how much more settled than was allowed\", and a negative money string is a\n * shape nothing else in the system accepts, so asking for one is a bug here\n * rather than a value to hand on.\n */\nexport function subDecimal(a: string, b: string): string {\n if (compareDecimal(a, b) < 0) {\n throw new RangeError(`subDecimal: ${a} is less than ${b}`);\n }\n const scale = Math.max(fractionLength(a), fractionLength(b));\n return fromScaled(toScaled(a, scale) - toScaled(b, scale), scale);\n}\n\n/** Multiply two decimal strings exactly (e.g. median * \"3\" for price-sanity). */\nexport function mulDecimal(a: string, b: string): string {\n assertDecimal(a);\n assertDecimal(b);\n const scaleA = fractionLength(a);\n const scaleB = fractionLength(b);\n const product = toScaled(a, scaleA) * toScaled(b, scaleB);\n return fromScaled(product, scaleA + scaleB);\n}\n\n/** Convenience: a > b. */\nexport function gt(a: string, b: string): boolean {\n return compareDecimal(a, b) === 1;\n}\n\n/** Convenience: a < b. */\nexport function lt(a: string, b: string): boolean {\n return compareDecimal(a, b) === -1;\n}\n","/**\n * Minimal, safe glob matcher supporting only the `*` wildcard (matches any run\n * of characters, including none). Used for vendor host allowlists\n * (e.g. \"*.trusted.io\"), agent-id targeting (e.g. \"agt_research_*\"), and\n * Gate route pricing (e.g. \"/api/reports/*\").\n *\n * Deliberately NOT a regex from user input — the pattern is escaped so a\n * malicious policy value cannot inject regex behavior (ReDoS, etc.).\n */\nexport function globMatch(pattern: string, value: string): boolean {\n const escaped = pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // escape regex metachars\n const withWildcard = escaped.replace(/\\\\\\*/g, '.*'); // re-enable only `*`\n return new RegExp(`^${withWildcard}$`).test(value);\n}\n\n/** True if `value` matches any pattern in the list. */\nexport function globMatchAny(patterns: readonly string[], value: string): boolean {\n return patterns.some((p) => globMatch(p, value));\n}\n","import { z } from 'zod';\nimport { AgentId, OrgId } from './ids.js';\nimport { Chain } from './chain.js';\n\n/**\n * Enforcement tier of a given agent wallet:\n * - `observed` — Rein sees spend on-chain but has no control (no SDK either).\n * - `sdk` — agent uses @reinconsole/sdk; advisory + observability (bypassable).\n * - `session-key` — signer-level scope; out-of-policy txs cannot be signed.\n */\nexport const EnforcementMode = z.enum(['observed', 'sdk', 'session-key']);\nexport type EnforcementMode = z.infer<typeof EnforcementMode>;\n\nexport const AgentWallet = z.object({\n chain: Chain,\n address: z.string().min(1),\n mode: EnforcementMode,\n});\nexport type AgentWallet = z.infer<typeof AgentWallet>;\n\nexport const AgentStatus = z.enum(['active', 'frozen']);\nexport type AgentStatus = z.infer<typeof AgentStatus>;\n\n/**\n * A semantic grouping label, e.g. \"research\" or \"prod-trading\". Lowercase\n * slugs only — policy `appliesTo.labels` matches these (with globs), and a\n * case-normalization mismatch would silently un-target a policy.\n */\nexport const AgentLabel = z\n .string()\n .max(40)\n .regex(/^[a-z0-9][a-z0-9-]*$/, 'label must be a lowercase slug like \"research\" or \"prod-trading\"');\nexport type AgentLabel = z.infer<typeof AgentLabel>;\n\nexport const Agent = z.object({\n id: AgentId,\n orgId: OrgId,\n name: z.string().min(1).max(200),\n /** On-chain ERC-8004 identity, if the agent is registered. */\n erc8004Id: z.string().optional(),\n /** Semantic grouping labels — the targets of policy `appliesTo.labels`. */\n labels: z\n .array(AgentLabel)\n .max(16)\n .refine((ls) => new Set(ls).size === ls.length, { message: 'labels must be unique' })\n .default([]),\n wallets: z.array(AgentWallet).default([]),\n status: AgentStatus.default('active'),\n createdAt: z.coerce.date(),\n});\nexport type Agent = z.infer<typeof Agent>;\n","import { z } from 'zod';\n\n/**\n * The canonical string form of an ERC-8004 agent identity:\n *\n * eip155:{chainId}:{identityRegistry}/{tokenId}\n *\n * The part before the `/` is the spec's registry reference verbatim (the\n * `agentRegistry` field of a registration file, CAIP-10 account form); the\n * tokenId is the ERC-721 id the Identity Registry minted (`agentId` in the\n * spec). This exact string is what `Agent.erc8004Id` / `Vendor.erc8004Id`\n * carry, AND the reputation-subject id a registered agent's evidence keys by.\n *\n * Casing is load-bearing: reputation subject keys for agent-kind ids are\n * case-sensitive unless they start with `0x` (see @reinconsole/graph\n * `normalizeSubject`), and an eip155 string does not. `formatErc8004Id`\n * therefore always emits the registry address in lowercase, and consumers must\n * never hand-build the string — parse then re-format to normalize.\n */\n\nexport interface Erc8004Ref {\n /** EIP-155 chain id (e.g. 8453 Base, 84532 Base Sepolia). */\n chainId: number;\n /** Identity Registry contract address, lowercase 0x-hex. */\n registry: string;\n /** ERC-721 tokenId — the spec's `agentId`. bigint: token ids exceed 2^53. */\n tokenId: bigint;\n}\n\nconst REGISTRY_PATTERN = /^0x[0-9a-fA-F]{40}$/;\nconst ID_PATTERN = /^eip155:(\\d+):(0x[0-9a-fA-F]{40})\\/(\\d+)$/;\n\n/** Build the canonical id string. Throws on a malformed ref (programmer error). */\nexport function formatErc8004Id(ref: Erc8004Ref): string {\n if (!Number.isSafeInteger(ref.chainId) || ref.chainId < 1) {\n throw new RangeError(`erc8004: bad chainId ${ref.chainId}`);\n }\n if (!REGISTRY_PATTERN.test(ref.registry)) {\n throw new RangeError(`erc8004: bad registry address ${ref.registry}`);\n }\n if (typeof ref.tokenId !== 'bigint' || ref.tokenId < 0n) {\n // The typeof guard matters for plain-JS callers: 1.5 < 0n is legal JS and\n // false, and would mint the unparseable \"…/1.5\".\n throw new RangeError(`erc8004: bad tokenId ${ref.tokenId}`);\n }\n return `eip155:${ref.chainId}:${ref.registry.toLowerCase()}/${ref.tokenId}`;\n}\n\n/**\n * Parse a canonical (or hand-written) id string. Accepts any address casing\n * and returns a lowercase ref, or undefined when malformed — so\n * `formatErc8004Id(parseErc8004Id(x)!)` is the normalization step.\n */\nexport function parseErc8004Id(value: string): Erc8004Ref | undefined {\n const match = ID_PATTERN.exec(value);\n if (!match) return undefined;\n const chainId = Number(match[1]);\n if (!Number.isSafeInteger(chainId) || chainId < 1) return undefined;\n return {\n chainId,\n registry: match[2]!.toLowerCase(),\n tokenId: BigInt(match[3]!),\n };\n}\n\n/** Strict boundary schema for the id string (the fields themselves stay free-form). */\nexport const Erc8004Id = z\n .string()\n .refine((value) => parseErc8004Id(value) !== undefined, {\n message: 'expected \"eip155:{chainId}:{registry}/{tokenId}\"',\n });\nexport type Erc8004Id = z.infer<typeof Erc8004Id>;\n","import { z } from 'zod';\nimport { AgentId, IntentId } from './ids.js';\nimport { Chain, Asset } from './chain.js';\nimport { DecimalString } from './money.js';\n\nexport const Vendor = z.object({\n host: z.string().min(1),\n address: z.string().min(1),\n erc8004Id: z.string().optional(),\n});\nexport type Vendor = z.infer<typeof Vendor>;\n\n/**\n * The observability gold: links every micro-payment back to the task and run\n * that caused it, so finance teams can answer \"what was this spend for?\".\n */\nexport const TaskContext = z.object({\n taskId: z.string().optional(),\n parentRunId: z.string().optional(),\n purpose: z.string().max(500).optional(),\n});\nexport type TaskContext = z.infer<typeof TaskContext>;\n\n/**\n * A request to spend, submitted to the policy engine BEFORE any signature is\n * released. The `nonce` provides replay protection on the signer path.\n */\nexport const PaymentIntent = z.object({\n id: IntentId,\n agentId: AgentId,\n vendor: Vendor,\n resource: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n taskContext: TaskContext.default({}),\n nonce: z.string().min(1),\n createdAt: z.coerce.date(),\n});\nexport type PaymentIntent = z.infer<typeof PaymentIntent>;\n\n/**\n * Reduce an intent's `resource` to its path for policy matching. The value is\n * vendor-declared and shape-varies: the x402 spec puts a full URL in\n * `requirement.resource`, while the guard falls back to the request pathname\n * when the vendor omits it. Policies match on the PATH so authors never need\n * to know which shape a vendor emits; host targeting stays with\n * `vendorHostIn`, which is derived from the real request URL rather than the\n * vendor's own claim. Query strings do not survive the reduction.\n */\nexport function resourcePathOf(resource: string): string {\n try {\n const url = new URL(resource);\n if (url.protocol === 'http:' || url.protocol === 'https:') return url.pathname;\n } catch {\n // Not a URL — already a bare path.\n }\n return resource;\n}\n","import { z } from 'zod';\nimport { AgentId, DecisionId, IntentId } from './ids.js';\n\nexport const DecisionOutcome = z.enum(['allow', 'deny', 'escalate']);\nexport type DecisionOutcome = z.infer<typeof DecisionOutcome>;\n\n/**\n * The signed, hash-chained record of a single policy evaluation. Written BEFORE\n * any signature is released. `prevHash` + `hash` form a tamper-evident chain;\n * `signature` is the policy service's signing key over `hash`. The eventual\n * on-chain `txHash` is attached later by the indexer (see SettledPayment).\n */\nexport const Decision = z.object({\n id: DecisionId,\n intentId: IntentId,\n /**\n * sha256 of the intent's canonical content (see canonical.ts). Binds the\n * decision to the exact transfer it judged — amount, recipient, asset,\n * chain — so a signer can verify an {intent, decision} pair offline as a\n * self-contained spend voucher, not just a reference by id.\n */\n intentHash: z.string(),\n outcome: DecisionOutcome,\n /** Ids of the rules that fired, for explainability. */\n matchedRules: z.array(z.string()).default([]),\n /** Human-readable explanation surfaced in the dashboard. */\n reason: z.string().optional(),\n policyId: z.string(),\n policyVersion: z.string(),\n /** Hash of the previous decision in the chain (tamper-evident log). */\n prevHash: z.string(),\n /** Hash of this decision's canonical content. */\n hash: z.string(),\n /** Service signing-key signature over `hash`. */\n signature: z.string(),\n latencyMs: z.number().nonnegative(),\n decidedAt: z.coerce.date(),\n});\nexport type Decision = z.infer<typeof Decision>;\n\n/**\n * A decision as `GET /v1/decisions` serves it (0.3.0): the signed record plus\n * `agentId`, the agent whose intent it judged.\n *\n * `agentId` is an ENVELOPE field -- the engine's own attribution, outside\n * `hash` and `signature`, so a verified chain vouches for nothing about it.\n * Putting it inside would have changed the hash of every future decision and\n * split the chain into two formats. It is here for a reconciler that is not\n * co-located with the agent: `reconcile()` needs an allowed intent's agent,\n * or agent B's payment against A's intent is credited to A (S74), and before\n * this field only `/v1/reconciliation` carried it -- and erases it the moment\n * a settlement lands. Absent on an unattributed (pre-tenancy) row.\n */\nexport const AttributedDecision = Decision.extend({ agentId: AgentId.optional() });\nexport type AttributedDecision = z.infer<typeof AttributedDecision>;\n","import type { PaymentIntent } from './intent.js';\nimport type { DecisionOutcome } from './decision.js';\nimport type { ApprovalVerdict } from './approval.js';\n\n/**\n * Canonical byte forms shared by the policy engine (which hashes and signs\n * them) and the signer (which verifies them offline). Field order is fixed,\n * dates are ISO strings, and absent optionals are explicit nulls, so the same\n * parsed object always canonicalizes to the same bytes — including after a\n * JSON round-trip over HTTP.\n *\n * These builders are pure (no node:crypto) so @reinconsole/core stays loadable in a\n * browser; services hash the strings with whatever sha256 they have.\n */\n\n/** Canonical form of an intent — what `Decision.intentHash` commits to. */\nexport function canonicalIntent(intent: PaymentIntent): string {\n return JSON.stringify({\n id: intent.id,\n agentId: intent.agentId,\n vendor: {\n host: intent.vendor.host,\n address: intent.vendor.address,\n erc8004Id: intent.vendor.erc8004Id ?? null,\n },\n resource: intent.resource,\n amount: intent.amount,\n asset: intent.asset,\n chain: intent.chain,\n taskContext: {\n taskId: intent.taskContext.taskId ?? null,\n parentRunId: intent.taskContext.parentRunId ?? null,\n purpose: intent.taskContext.purpose ?? null,\n },\n nonce: intent.nonce,\n createdAt: intent.createdAt.toISOString(),\n });\n}\n\n/** The decision fields covered by `Decision.hash` (and thus its signature). */\nexport interface DecisionContent {\n intentId: string;\n intentHash: string;\n outcome: DecisionOutcome;\n matchedRules: string[];\n policyId: string;\n policyVersion: string;\n prevHash: string;\n decidedAt: Date;\n}\n\n/** Canonical form of a decision — what `Decision.hash` is computed over. */\nexport function canonicalDecision(d: DecisionContent): string {\n return JSON.stringify({\n intentId: d.intentId,\n intentHash: d.intentHash,\n outcome: d.outcome,\n matchedRules: d.matchedRules,\n policyId: d.policyId,\n policyVersion: d.policyVersion,\n prevHash: d.prevHash,\n decidedAt: d.decidedAt.toISOString(),\n });\n}\n\n/** The fields an approval signature commits to. */\nexport interface ApprovalContent {\n decisionId: string;\n intentHash: string;\n verdict: ApprovalVerdict;\n}\n\n/**\n * Canonical form of an approval — the exact bytes an approver signs.\n *\n * Three properties are deliberate. The leading `rein` domain tag separates\n * this signature space from the decision log's, so a decision signature can\n * never be replayed as an approval or the reverse. `decisionId` makes each\n * challenge single-use: one escalation, one decision id, one signature that\n * means anything. And `verdict` is INSIDE the signed bytes — without it, a\n * captured approval could be resubmitted as a rejection (or vice versa) by\n * anyone who saw it in flight.\n */\nexport function canonicalApproval(a: ApprovalContent): string {\n return JSON.stringify({\n rein: 'approval/v1',\n decisionId: a.decisionId,\n intentHash: a.intentHash,\n verdict: a.verdict,\n });\n}\n","import { z } from 'zod';\nimport { AgentId, SessionId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/**\n * A session-key grant: the signer tier's unit of delegated authority. The\n * agent process holds only the bearer token — the wallet key never leaves the\n * signer — and the stored record keeps a hash of the token, never the token\n * itself (it is returned exactly once, at creation).\n *\n * Caps here are the signer-side backstop UNDER whatever policy says: a\n * signature is released only when the engine allowed the intent AND the\n * session has room. Amounts are decimal strings in the asset's human units.\n */\nexport const Session = z.object({\n id: SessionId,\n agentId: AgentId,\n /** sha256 hex of the bearer token. */\n tokenHash: z.string(),\n /** Cumulative ceiling across the session's lifetime. Absent = uncapped. */\n capAmount: DecimalString.optional(),\n /** Ceiling per individual signature. Absent = uncapped. */\n maxPerPayment: DecimalString.optional(),\n expiresAt: z.coerce.date(),\n createdAt: z.coerce.date(),\n revokedAt: z.coerce.date().optional(),\n});\nexport type Session = z.infer<typeof Session>;\n","import { z } from 'zod';\nimport { IntentId } from './ids.js';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\n\n/**\n * On-chain confirmation of a settled payment, reconciled by the indexer against\n * the originating intent (amount + recipient + nonce, or facilitator webhook).\n */\nexport const SettledPayment = z.object({\n intentId: IntentId,\n txHash: z.string(),\n chain: Chain,\n blockNumber: z.coerce.bigint(),\n facilitator: z.string().optional(),\n feePaid: DecimalString.optional(),\n confirmedAt: z.coerce.date(),\n});\nexport type SettledPayment = z.infer<typeof SettledPayment>;\n\n/**\n * A report that an allowed intent's payment actually landed — the settlement\n * half of reconciliation (\"allowed but never settled\", B1).\n *\n * Deliberately looser than {@link SettledPayment}: the on-chain confirmation\n * requires a tx hash and a block, while a REPORT is whatever the observer\n * could see. A vendor's `X-PAYMENT-RESPONSE` names a transaction but no block;\n * a facilitator webhook may name neither. Demanding the strong shape would\n * mean the weakest observers — the ones a deployment actually has — could\n * report nothing at all, and every allowance would read as a gap.\n *\n * `source` records WHO said so, because the evidence is not all equal: an\n * independent indexer watching the chain is the strong claim, while a guard\n * reporting on its own payment is the agent vouching for itself. Nothing here\n * grants authority — a settlement report can only ever CLOSE a gap in the\n * reconciliation report, never authorize a payment or alter a decision.\n */\nexport const SettlementReport = z.object({\n intentId: IntentId,\n /** The settling transaction, when the observer saw one. */\n txHash: z.string().optional(),\n chain: Chain.optional(),\n /** The amount as SETTLED, when known — may differ from the amount allowed. */\n amount: DecimalString.optional(),\n /** Free-form observer tag, e.g. \"indexer\", \"guard\", \"facilitator\". */\n source: z.string().min(1).max(64).optional(),\n confirmedAt: z.coerce.date(),\n});\nexport type SettlementReport = z.infer<typeof SettlementReport>;\n","import { z } from 'zod';\nimport { ReceiptId, IntentId, DecisionId, AgentId } from './ids.js';\nimport { Chain, Asset } from './chain.js';\nimport { DecimalString } from './money.js';\nimport { TaskContext } from './intent.js';\nimport { DecisionOutcome } from './decision.js';\n\n/**\n * What the SDK reports back after settlement: the `X-PAYMENT-RESPONSE` header\n * a vendor returns once payment clears. Mock facilitators fill this in v0.1;\n * the indexer reconciles it against on-chain truth later (see SettledPayment).\n */\nexport const ReceiptSettlement = z.object({\n txHash: z.string().optional(),\n networkId: z.string().optional(),\n /** Raw header payload, kept for forensics until the indexer confirms. */\n raw: z.string().optional(),\n});\nexport type ReceiptSettlement = z.infer<typeof ReceiptSettlement>;\n\n/**\n * The SDK-side record of one guarded payment attempt: which request triggered\n * it, what the engine decided, and (if allowed and paid) how it settled.\n * Receipts are the observability primitive — every 402 the guard touches\n * produces exactly one, whether the payment was allowed or blocked.\n */\nexport const Receipt = z.object({\n id: ReceiptId,\n agentId: AgentId,\n intentId: IntentId,\n decisionId: DecisionId,\n outcome: DecisionOutcome,\n /** The URL the agent actually fetched (the paywalled resource). */\n url: z.string(),\n method: z.string().default('GET'),\n vendorHost: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n taskContext: TaskContext.default({}),\n /** Human-readable explanation copied from the decision. */\n reason: z.string().optional(),\n /** Present only once a payment was made and the vendor confirmed it. */\n settlement: ReceiptSettlement.optional(),\n createdAt: z.coerce.date(),\n});\nexport type Receipt = z.infer<typeof Receipt>;\n","import { z } from 'zod';\nimport { GateReceiptId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/**\n * The vendor-side twin of `Receipt`: one settled x402 payment as the GATE saw\n * it — who paid, for which priced route, and the settlement transaction. The\n * agent-side Receipt records what an agent tried to spend; a GateReceipt\n * records what a vendor actually earned.\n */\nexport const GateReceipt = z.object({\n id: GateReceiptId,\n at: z.coerce.date(),\n /** The route pattern that priced this request, e.g. \"/api/reports/*\". */\n route: z.string().min(1),\n /** The concrete resource paid for (URL path actually requested). */\n resource: z.string().min(1),\n method: z.string().min(1),\n /** The paying wallet address, as asserted by the settled payment. */\n payer: z.string().min(1),\n payTo: z.string().min(1),\n /** Human-unit decimal amount, e.g. \"0.05\". */\n amount: DecimalString,\n /** The same amount in the asset's atomic units, e.g. \"50000\". */\n amountAtomic: z.string().regex(/^\\d+$/, 'atomic amount must be an integer string'),\n /** Token symbol or contract address, exactly as quoted in the requirement. */\n asset: z.string().min(1),\n network: z.string().min(1),\n /** Settlement transaction hash (mock ledger or on-chain). */\n transaction: z.string().min(1),\n});\nexport type GateReceipt = z.infer<typeof GateReceipt>;\n","import { z } from 'zod';\nimport { Chain } from './chain.js';\nimport { OrgId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/** A rolling-window spec, e.g. \"24h\", \"1h\", \"30m\", \"7d\". */\nexport const Window = z\n .string()\n .regex(/^\\d+[smhd]$/, 'window must be like \"30s\", \"15m\", \"1h\", or \"7d\"');\nexport type Window = z.infer<typeof Window>;\n\n/** A relative multiplier, e.g. \"3x\" (used for price-sanity checks). */\nexport const Multiplier = z.string().regex(/^\\d+(\\.\\d+)?x$/, 'multiplier must be like \"3x\"');\nexport type Multiplier = z.infer<typeof Multiplier>;\n\n/**\n * The set of predicates a rule can test. Multiple predicates in one condition\n * are ANDed together. Each maps to an evaluator in the policy engine.\n */\nexport const Condition = z\n .object({\n /** Per-transaction amount exceeds this value. */\n amountGt: DecimalString.optional(),\n /** Sum of spend in a rolling window exceeds `gt`. */\n rollingSum: z.object({ window: Window, gt: DecimalString }).optional(),\n /** Transaction count in a rolling window exceeds `gt`. */\n txCount: z.object({ window: Window, gt: z.number().int().nonnegative() }).optional(),\n /** Vendor host matches one of these patterns (supports `*` globs). */\n vendorHostIn: z.array(z.string()).optional(),\n /**\n * Resource PATH matches one of these patterns (supports `*` globs, e.g.\n * \"/v1/reports/*\"). A full-URL resource is reduced to its pathname before\n * matching (see `resourcePathOf`), so the same pattern works whether the\n * vendor declares \"https://api.x.com/v1/answer\" or \"/v1/answer\". Pair\n * with `vendorHostIn` to scope a path rule to specific vendors.\n */\n resourceIn: z.array(z.string()).optional(),\n /** First time Rein has seen this vendor for the agent. */\n vendorFirstSeen: z.boolean().optional(),\n /** Vendor reputation score is below this threshold (Phase 3 hook). */\n vendorReputationLt: z.number().min(0).max(100).optional(),\n /** Amount is more than `gt`-times the observed median for this resource. */\n amountVsResourceMedian: z.object({ gt: Multiplier }).optional(),\n /**\n * Cumulative spend attributed to THIS intent's `taskContext.taskId`\n * (prior spend on the task plus this payment) exceeds `gt`. Scopes a\n * budget to one unit of work rather than to a window: a research run that\n * is meant to cost a dollar cannot quietly cost fifty, however slowly.\n *\n * An intent carrying no `taskId` cannot be attributed to a task, so the\n * predicate never triggers for one. Requiring attribution is a separate,\n * deliberate rule (deny on `taskIdMissing`), not a side effect of setting\n * a budget — otherwise every untagged probe payment would trip every\n * task budget in the policy.\n */\n taskBudget: z.object({ gt: DecimalString }).optional(),\n /** The intent carries no `taskContext.taskId` (or an empty one). */\n taskIdMissing: z.boolean().optional(),\n })\n .refine((c) => Object.values(c).some((v) => v !== undefined), {\n message: 'a condition must specify at least one predicate',\n });\nexport type Condition = z.infer<typeof Condition>;\n\n/**\n * A behavioral circuit breaker: one primitive with three tripwires — a window\n * (rate), a transaction count, and a value cap — measured over the agent's\n * own recent activity.\n *\n * A breaker is not a rule. Rules ask about the intent in front of them; a\n * breaker asks whether the agent's BEHAVIOR has left the envelope it was\n * given, and once it has, every subsequent intent ESCALATES for a signed\n * approval. It never denies on its own: an agent that trips a breaker in the\n * middle of a job must be able to be waved through by a human, because a\n * silent deny at the wrong moment strands the work with no path forward and\n * no one told (the failure mode Flash's exit paths taught).\n *\n * Reset happens two ways, and they are one mechanism: the window rolling\n * forward, or a signed approval, which moves the breaker's counting floor to\n * now. Nothing \"un-trips\" a breaker by decree — the activity simply stops\n * being inside the measured span.\n *\n * Targeting is the policy's job: `appliesTo.agents` / `appliesTo.labels`\n * decide WHICH agents carry the breaker. COUNTING is always per agent — a\n * breaker pooled across every agent sharing a label needs a cross-agent\n * aggregate the per-agent spend context cannot express, and is deferred.\n */\nexport const Breaker = z\n .object({\n id: z.string().min(1),\n /** The trailing span the tripwires measure over. */\n window: Window,\n /**\n * Trip when the window would hold MORE than this many transactions.\n * `10` permits ten in the window and escalates the eleventh.\n */\n txCount: z.number().int().positive().optional(),\n /**\n * Trip when the window's spend would exceed this total. `\"50.00\"` permits\n * a window totalling exactly 50.00 and escalates the payment that would\n * carry it past. Prospective, like every other spend predicate: the\n * payment that would breach the cap is the one that escalates, rather\n * than the innocent one after it.\n */\n valueCap: DecimalString.optional(),\n })\n .refine((b) => b.txCount !== undefined || b.valueCap !== undefined, {\n message: 'a breaker must specify at least one of txCount / valueCap',\n });\nexport type Breaker = z.infer<typeof Breaker>;\n\n/**\n * A single rule: an id plus exactly one action (allow / deny / escalate) whose\n * value is the condition that triggers it.\n */\nexport const Rule = z\n .object({\n id: z.string().min(1),\n allow: Condition.optional(),\n deny: Condition.optional(),\n escalate: Condition.optional(),\n })\n .refine((r) => [r.allow, r.deny, r.escalate].filter((v) => v !== undefined).length === 1, {\n message: 'a rule must specify exactly one of allow / deny / escalate',\n });\nexport type Rule = z.infer<typeof Rule>;\n\nexport const PolicyDefault = z.enum(['allow', 'deny']);\nexport type PolicyDefault = z.infer<typeof PolicyDefault>;\n\nexport const AppliesTo = z.object({\n /** Agent id patterns (supports `*` globs, e.g. \"agt_research_*\"). */\n agents: z.array(z.string()).optional(),\n /**\n * Agent label patterns (supports `*` globs) — semantic targeting. Matches\n * when the agent carries at least one label matching any pattern. Requires\n * the agent DOCUMENT: an intent from an unregistered agent never matches a\n * labels-targeted policy (and so fails closed when no other policy applies).\n */\n labels: z.array(z.string()).optional(),\n chains: z.array(Chain).optional(),\n});\nexport type AppliesTo = z.infer<typeof AppliesTo>;\n\nexport const Escalation = z.object({\n approvers: z.array(z.string()).default([]),\n timeoutAction: PolicyDefault.default('deny'),\n timeoutMin: z.number().int().positive().default(15),\n});\nexport type Escalation = z.infer<typeof Escalation>;\n\n/**\n * A declarative, versioned, immutable-once-active policy. Evaluation order in\n * the engine is: explicit DENY > ESCALATE > ALLOW > `default`.\n */\nexport const Policy = z.object({\n policyId: z.string().min(1),\n version: z.string().default('1'),\n /**\n * The org this policy governs. ABSENT means GLOBAL — it is considered for\n * every agent, which is what every policy written before tenancy existed is\n * and stays.\n *\n * This is separate from {@link AppliesTo} on purpose, and it has to be:\n * `appliesTo: {}` matches every agent, so a tenant's policy carrying the\n * default targeting would otherwise govern other tenants' agents the moment\n * it was written. Org scoping is applied BEFORE targeting — a policy whose\n * `orgId` is not the agent's org is not a candidate at all.\n */\n orgId: OrgId.optional(),\n appliesTo: AppliesTo.default({}),\n rules: z.array(Rule).default([]),\n /**\n * Behavioral breakers evaluated alongside the rules. A tripped breaker\n * escalates at the same precedence as an `escalate` rule — so an explicit\n * DENY still wins, and an ALLOW rule cannot wave past a tripped breaker.\n */\n breakers: z.array(Breaker).default([]),\n default: PolicyDefault.default('deny'),\n /** Below this amount, fail-open is permitted during a policy-service outage. */\n denyFloor: DecimalString.default('0.05'),\n escalation: Escalation.optional(),\n});\nexport type Policy = z.infer<typeof Policy>;\n","import { z } from 'zod';\nimport { AgentId } from './ids.js';\nimport { Window } from './policy.js';\n\n/**\n * Dead-man monitoring: the alarm for an agent that stopped (Phase B2).\n *\n * Every other control in Rein answers \"should this payment happen?\". This one\n * answers the question nothing else in the stack asks: an agent that has gone\n * quiet raises no intent, breaks no budget, and trips no breaker — it simply\n * disappears, and a control plane watching only for bad payments will report\n * a perfectly clean month while the work silently stopped being done. (Flash\n * lost four detectors this way, none of which announced anything.)\n *\n * Three rules hold the design honest, and each is the direct mirror of a rule\n * B1 already established:\n *\n * 1. An expectation is DECLARED, never inferred. Most agents are episodic:\n * they run when there is work. Silence is only evidence for an agent\n * somebody said should be periodic, so an unwatched agent has no liveness\n * state at all — the same rule as `taskBudget`, which never fires on an\n * intent that carries no task id.\n * 2. Silence has an AGE, not a boolean — `late` before `missing` — exactly as\n * a missing settlement is `in-flight` before it is `unsettled`.\n * 3. The engine never alarms about silence it did not witness. An engine that\n * was down cannot tell a dead agent from its own outage, so a silence\n * older than this process reads `unknown` until the process has been up\n * long enough to certify it. B1 refuses to manufacture gaps out of records\n * it cannot join; this refuses to manufacture alarms out of downtime.\n *\n * Liveness is observability and carries no authority whatsoever: it cannot\n * deny, escalate, freeze, or alter any decision. A missing agent is news for\n * a human, not an input to policy.\n */\n\n/** How the engine came to know an agent was alive. */\nexport const LivenessSource = z.enum([\n /** It submitted an intent — ANY outcome, including a denial. */\n 'intent',\n /** It reported in explicitly, having nothing to buy. */\n 'heartbeat',\n]);\nexport type LivenessSource = z.infer<typeof LivenessSource>;\n\n/** Default slack past the interval before silence becomes an alarm. */\nexport const DEFAULT_LIVENESS_GRACE_MS = 60_000;\n\n/**\n * \"This agent is supposed to check in at least this often.\"\n *\n * `graceMs` is what keeps a jittery-but-healthy agent out of the alert\n * channel: an agent on a 15m cadence that lands at 15m04s is not a dead agent,\n * and an alarm that cries at every wobble is one an operator learns to ignore\n * — which is the failure mode that loses the detector nobody was watching.\n */\nexport const LivenessExpectation = z.object({\n agentId: AgentId,\n /** Longest silence still considered normal, e.g. '15m'. */\n interval: Window,\n /** Extra slack past `interval` before the silence is an alarm. */\n graceMs: z.number().int().nonnegative().default(DEFAULT_LIVENESS_GRACE_MS),\n /**\n * When watching began. An agent that has NEVER been seen is measured from\n * here, so a watch on an agent that never starts does raise an alarm — a\n * detector that never came up is exactly as dead as one that stopped.\n */\n since: z.coerce.date(),\n /** What this agent is supposed to be doing, shown beside the alarm. */\n note: z.string().max(200).optional(),\n});\nexport type LivenessExpectation = z.infer<typeof LivenessExpectation>;\n\n/** The registration payload; the engine stamps `since`. */\nexport const LivenessWatchInput = LivenessExpectation.omit({ since: true }).extend({\n graceMs: z.number().int().nonnegative().optional(),\n});\nexport type LivenessWatchInput = z.input<typeof LivenessWatchInput>;\n\n/**\n * An out-of-band \"I am alive\" from an agent with nothing to buy.\n *\n * The engine sees every intent, so a spending agent needs no heartbeat at all.\n * This exists for the agent that is working and simply not paying for\n * anything — without it, a dead-man alarm is really a no-spend alarm, and it\n * would page a human every time an agent had a quiet afternoon.\n *\n * It authorizes nothing. The only thing a heartbeat can change is a row in the\n * liveness report; it cannot lift a breaker, a budget, or a freeze.\n */\nexport const Heartbeat = z.object({\n agentId: AgentId,\n /** When the agent was alive. Defaults to arrival time. */\n at: z.coerce.date().optional(),\n /** Free-form note from the reporter, e.g. \"poll cycle 41 ok\". */\n note: z.string().max(200).optional(),\n});\nexport type Heartbeat = z.infer<typeof Heartbeat>;\n\n/**\n * Where an agent stands against its expectation.\n *\n * - `alive` — seen within the interval.\n * - `late` — past the interval, inside the grace. Not yet news.\n * - `missing` — past both, and this process watched it happen. THE alarm.\n * - `unknown` — past both, but the engine has not been up long enough to have\n * witnessed the silence. Not an alarm: an engine that restarted\n * five minutes ago knows nothing about the last three days.\n */\nexport const LivenessStatus = z.enum(['alive', 'late', 'missing', 'unknown']);\nexport type LivenessStatus = z.infer<typeof LivenessStatus>;\n","import { z } from 'zod';\nimport { AgentId, ApproverKeyId, DecisionId, IntentId, OrgId } from './ids.js';\nimport { Asset, Chain } from './chain.js';\nimport { DecimalString } from './money.js';\n\n/**\n * Human approval of an escalated payment.\n *\n * The load-bearing rule: an approval is a SIGNATURE over the decision, made by\n * a key registered with the engine. The delivery channel (Telegram, email,\n * whatever) is transport, never authority — anyone who compromises the channel\n * gets to show a human a message, not to release funds. There is deliberately\n * no click-to-approve path.\n */\n\nexport const ApprovalVerdict = z.enum(['approve', 'reject']);\nexport type ApprovalVerdict = z.infer<typeof ApprovalVerdict>;\n\n/**\n * `pending` is the only non-terminal state. `expired` is what a TTL lapse\n * converts to — indistinguishable from `rejected` in effect (both deny), but\n * distinguished in the record so \"nobody answered\" and \"somebody said no\" stay\n * separable in an audit.\n */\nexport const ApprovalStatus = z.enum(['pending', 'approved', 'rejected', 'expired']);\nexport type ApprovalStatus = z.infer<typeof ApprovalStatus>;\n\n/**\n * A key whose signature the engine accepts as an approval. Public half only —\n * the private half lives with the human (a hardware token, a laptop, an\n * offline signer) and must never reach the engine.\n */\nexport const ApproverKey = z.object({\n id: ApproverKeyId,\n orgId: OrgId,\n name: z.string().min(1).max(200),\n /** ed25519 public key, SPKI PEM — the same encoding as the decision log's. */\n publicKey: z.string().min(1),\n algorithm: z.literal('ed25519').default('ed25519'),\n createdAt: z.coerce.date(),\n revokedAt: z.coerce.date().optional(),\n});\nexport type ApproverKey = z.infer<typeof ApproverKey>;\n\n/**\n * A parked escalation: the payment the engine refused to allow on its own\n * authority, held open until a signature arrives or the TTL lapses. Keyed by\n * the escalating decision — one decision, at most one approval request.\n *\n * The payment facts are copied in rather than referenced so the challenge a\n * human sees is self-contained: amount, vendor and resource are exactly what\n * they are being asked to authorize.\n */\nexport const ApprovalRequest = z.object({\n /** The `escalate` decision this request answers (primary key). */\n decisionId: DecisionId,\n intentId: IntentId,\n /** Binds the approval to the exact transfer — the signed bytes commit to it. */\n intentHash: z.string(),\n agentId: AgentId,\n /**\n * The org the escalating agent belongs to, stamped when the request is\n * parked. ABSENT means the agent was never registered (or the request\n * predates tenancy), and such a request is answerable only by an unscoped\n * operator — there is no org to check an approver against, and guessing one\n * is how a cross-tenant approval happens.\n */\n orgId: OrgId.optional(),\n vendorHost: z.string(),\n resource: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n /**\n * Task attribution copied from the intent. Carried so an approved payment\n * still counts against its `taskBudget` — an escalation that lost its task\n * id on the way through would make the budget silently under-count exactly\n * the payments a human had to look at.\n */\n taskId: z.string().optional(),\n /** Why policy escalated, verbatim from the decision. */\n reason: z.string(),\n /**\n * Breaker ids whose trip contributed to this escalation. An approval of\n * this request resets exactly these — moving their counting floor to the\n * approval instant — so a human waving one payment through also clears the\n * behavior that stopped it, rather than being asked again immediately.\n * Empty when ordinary rules did the escalating.\n */\n breakers: z.array(z.string()).default([]),\n status: ApprovalStatus,\n createdAt: z.coerce.date(),\n /** Past this instant the request denies, whatever arrives afterwards. */\n expiresAt: z.coerce.date(),\n resolvedAt: z.coerce.date().optional(),\n /** Which registered key signed — absent when the TTL lapsed instead. */\n approverKeyId: ApproverKeyId.optional(),\n /**\n * The follow-up decision appended to the chain when this resolved. That\n * decision, not this record, is the voucher a signer verifies: the original\n * `escalate` decision is never rewritten.\n */\n finalDecisionId: DecisionId.optional(),\n});\nexport type ApprovalRequest = z.infer<typeof ApprovalRequest>;\n\n/** The two byte-strings an approver may sign for a given request. */\nexport const ApprovalChallenges = z.object({\n approve: z.string(),\n reject: z.string(),\n});\nexport type ApprovalChallenges = z.infer<typeof ApprovalChallenges>;\n\n/**\n * A signed verdict, submitted to the engine by anyone (the signature is the\n * authority, so the carrier does not matter). `signature` is base64 ed25519\n * over `canonicalApproval({ decisionId, intentHash, verdict })`.\n */\nexport const ApprovalGrant = z.object({\n decisionId: DecisionId,\n intentHash: z.string(),\n verdict: ApprovalVerdict,\n approverKeyId: ApproverKeyId,\n signature: z.string().min(1),\n});\nexport type ApprovalGrant = z.infer<typeof ApprovalGrant>;\n\n/** Terminal statuses — a request in one of these will never change again. */\nexport function isResolved(status: ApprovalStatus): boolean {\n return status !== 'pending';\n}\n","import { z } from 'zod';\n\nexport const ReputationSubject = z.object({\n kind: z.enum(['agent', 'vendor']),\n id: z.string(),\n});\nexport type ReputationSubject = z.infer<typeof ReputationSubject>;\n\nexport const ReputationComponents = z.object({\n volume: z.number(),\n longevity: z.number(),\n disputeRate: z.number(),\n counterpartyQuality: z.number(),\n settlementReliability: z.number(),\n});\nexport type ReputationComponents = z.infer<typeof ReputationComponents>;\n\n/**\n * A reputation score for an agent or vendor (Phase 3 — Graph). `confidence` is\n * first-class: a thin-history subject returns LOW confidence rather than a\n * misleadingly high score, so policy can avoid both false trust and unfair\n * freezing.\n */\nexport const ReputationScore = z.object({\n subject: ReputationSubject,\n score: z.number().min(0).max(100),\n components: ReputationComponents,\n confidence: z.number().min(0).max(1),\n asOf: z.coerce.date(),\n /** Hash-anchored attestation, optionally published on-chain (ERC-8004). */\n evidenceUri: z.string().optional(),\n});\nexport type ReputationScore = z.infer<typeof ReputationScore>;\n","import { z } from 'zod';\nimport { AgentId, DecisionId, IntentId, SessionId } from './ids.js';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\nimport { PaymentIntent } from './intent.js';\nimport { Decision } from './decision.js';\nimport { SettledPayment } from './payment.js';\nimport { GateReceipt } from './gate-receipt.js';\nimport { ApprovalRequest } from './approval.js';\nimport { LivenessExpectation, LivenessSource } from './liveness.js';\n\n/**\n * The canonical event envelope published on the bus (NATS in production).\n * `shadow.spend` is emitted when the indexer sees on-chain spend from a managed\n * wallet with NO corresponding ALLOW decision — the bypass signal that catches\n * SDK-mode evasion and drives the upgrade to the signer tier.\n * `signature.released` / `signature.refused` are that tier's heartbeat: every\n * time the signer does or does not put a key to work, the bus knows why.\n * `gate.*` is the vendor side of the wire: every quote a Gate issues, every\n * payment it accepts, every payment it turns away.\n * `approval.*` is the human-in-the-loop tier: an escalated intent parked for a\n * signature, and the signed (or lapsed) verdict that closed it.\n * `liveness.*` is the dead-man tier (B2), and the only pair here emitted by\n * something NOT happening: a watched agent that went quiet past its expected\n * interval, and the sighting that ended the silence.\n */\nexport const ReinEvent = z.discriminatedUnion('type', [\n z.object({ type: z.literal('intent.created'), at: z.coerce.date(), intent: PaymentIntent }),\n z.object({ type: z.literal('decision.made'), at: z.coerce.date(), decision: Decision }),\n z.object({ type: z.literal('payment.settled'), at: z.coerce.date(), payment: SettledPayment }),\n z.object({\n type: z.literal('shadow.spend'),\n at: z.coerce.date(),\n agentId: AgentId,\n txHash: z.string(),\n chain: Chain,\n amount: DecimalString,\n }),\n z.object({\n type: z.literal('signature.released'),\n at: z.coerce.date(),\n sessionId: SessionId,\n agentId: AgentId,\n intentId: IntentId,\n decisionId: DecisionId,\n amount: DecimalString,\n }),\n z.object({\n type: z.literal('signature.refused'),\n at: z.coerce.date(),\n /** Refusal code, e.g. \"decision_replayed\" (see @reinconsole/signer). */\n code: z.string(),\n reason: z.string(),\n sessionId: SessionId.optional(),\n agentId: AgentId.optional(),\n intentId: IntentId.optional(),\n }),\n z.object({\n type: z.literal('gate.quoted'),\n at: z.coerce.date(),\n resource: z.string(),\n method: z.string(),\n amount: DecimalString,\n asset: z.string(),\n network: z.string(),\n }),\n z.object({ type: z.literal('gate.settled'), at: z.coerce.date(), receipt: GateReceipt }),\n z.object({\n type: z.literal('approval.requested'),\n at: z.coerce.date(),\n request: ApprovalRequest,\n }),\n z.object({\n type: z.literal('approval.resolved'),\n at: z.coerce.date(),\n request: ApprovalRequest,\n /** The follow-up allow/deny decision this resolution appended. */\n decision: Decision,\n }),\n z.object({\n type: z.literal('liveness.missing'),\n at: z.coerce.date(),\n agentId: AgentId,\n expectation: LivenessExpectation,\n /** How long the agent has been silent when the alarm was raised. */\n silentMs: z.number().int().nonnegative(),\n /** Last sighting, absent when the agent was never seen at all. */\n lastSeenAt: z.coerce.date().optional(),\n }),\n z.object({\n type: z.literal('liveness.recovered'),\n at: z.coerce.date(),\n agentId: AgentId,\n /** How long the silence lasted, measured to this sighting. */\n silentMs: z.number().int().nonnegative(),\n source: LivenessSource,\n }),\n z.object({\n type: z.literal('gate.refused'),\n at: z.coerce.date(),\n /** Refusal code, e.g. \"payment_replayed\" (see @reinconsole/gate). */\n code: z.string(),\n reason: z.string(),\n resource: z.string(),\n /** Known only when the payment header decoded far enough to name a payer. */\n payer: z.string().optional(),\n }),\n]);\nexport type ReinEvent = z.infer<typeof ReinEvent>;\n","import { formatErc8004Id, parseErc8004Id, sumDecimal, type ReputationSubject } from '@reinconsole/core';\n\n/** A value that may be produced synchronously or awaited (durable writes). */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** One settled-money edge to a counterparty (the \"graph\" in reputation graph). */\nexport interface CounterpartyLine {\n settled: number;\n /** Settled decimal volume across this edge. */\n volume: string;\n}\n\n/**\n * Everything the graph has observed about one subject. Counters are raw and\n * append-only — scoring (scoring.ts) is a pure function over this record, so a\n * score is always re-derivable and explainable from the evidence behind it.\n */\nexport interface SubjectEvidence {\n subject: ReputationSubject;\n firstSeenMs: number;\n lastSeenMs: number;\n /** Payment attempts observed (allowed intents, presented payments). */\n attempts: number;\n /** Confirmed settlements (indexer-reconciled or gate-receipted). */\n settled: number;\n /** Settled decimal volume. */\n volume: string;\n /** Refusals by code (gate refusal codes, signer refusal codes). */\n refusals: Record<string, number>;\n /** Managed-wallet spend with no ALLOW decision behind it (bypass). */\n shadowSpends: number;\n /** Manual out-of-band reports (chargebacks, fraud complaints). */\n disputes: number;\n /** Manual out-of-band vouches. */\n endorsements: number;\n /** Who this subject has settled money with, keyed by subjectKey(). */\n counterparties: Map<string, CounterpartyLine>;\n}\n\n/**\n * Canonical subject identity. Hosts and EVM addresses are case-insensitive,\n * so they normalize to lowercase; ULID agent ids are case-sensitive and pass\n * through. ERC-8004 ids (`eip155:{chainId}:{registry}/{tokenId}`) normalize\n * via parse->format — @reinconsole/erc8004 always emits the canonical form, but this\n * is the graph's OWN boundary (remote producers hit POST /v1/links with\n * hand-built strings), and a checksummed registry or zero-padded tokenId\n * variant must not mint a split subject row. Note the two id spaces under\n * kind \"agent\": the engine names agents by ULID, a gate names them by paying\n * wallet — ERC-8004 linking (S17) folds both into the on-chain identity.\n */\nexport function normalizeSubject(subject: ReputationSubject): ReputationSubject {\n if (subject.id.startsWith('eip155:')) {\n const ref = parseErc8004Id(subject.id);\n if (ref) return { kind: subject.kind, id: formatErc8004Id(ref) };\n // Malformed eip155-ish strings fall through to the plain rules below.\n }\n const id =\n subject.kind === 'vendor' || subject.id.startsWith('0x') || subject.id.startsWith('0X')\n ? subject.id.toLowerCase()\n : subject.id;\n return { kind: subject.kind, id };\n}\n\nexport function subjectKey(subject: ReputationSubject): string {\n const normal = normalizeSubject(subject);\n return `${normal.kind}:${normal.id}`;\n}\n\n/**\n * The evidence store seam the graph depends on. Writes may be asynchronous (a\n * durable ledger persists behind them); reads are synchronous from the\n * hydrated working set so `score()` / `payerCheck()` stay sync in the gate hot\n * path. Mutations are explicit, atomic operations rather than a returned record\n * the caller mutates — that is what lets a durable implementation persist them.\n */\nexport interface EvidenceLedgerPort {\n /** A payment attempt (allowed intent / presented payment). */\n recordAttempt(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /** A confirmed settlement between two subjects (credits both sides + the edge). */\n recordSettlement(\n a: ReputationSubject,\n b: ReputationSubject,\n amount: string,\n atMs: number,\n ): MaybePromise<void>;\n /** A refusal against a subject, by code. */\n recordRefusal(subject: ReputationSubject, code: string, atMs: number): MaybePromise<void>;\n /** Managed-wallet spend with no ALLOW behind it. */\n recordShadowSpend(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /** An out-of-band dispute. */\n recordDispute(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /** An out-of-band endorsement. */\n recordEndorsement(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /**\n * Fold everything known about `alias` into `canonical` and forget the alias:\n * counters sum, volumes add, first/last seen widen, and settled-money edges\n * re-key on BOTH endpoints (peers that pointed at the alias now point at the\n * canonical subject). No-op when the alias has no record — which makes\n * re-asserting links at boot idempotent. This is the identity-linking\n * primitive (ERC-8004: one on-chain identity, many addresses/ids).\n */\n merge(canonical: ReputationSubject, alias: ReputationSubject): MaybePromise<void>;\n get(subject: ReputationSubject): SubjectEvidence | undefined;\n getByKey(key: string): SubjectEvidence | undefined;\n all(): IterableIterator<SubjectEvidence>;\n readonly size: number;\n /**\n * Await any pending durable writes (no-op in memory). Durable\n * implementations surface write failures HERE — the graph fire-and-forgets\n * its writes (a bus handler cannot await), so flush() is the error channel.\n */\n flush?(): Promise<void>;\n}\n\n/** The evidence store: one record per subject, created on first sighting. */\nexport class EvidenceLedger implements EvidenceLedgerPort {\n private readonly records = new Map<string, SubjectEvidence>();\n\n recordAttempt(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).attempts += 1;\n }\n\n recordSettlement(\n a: ReputationSubject,\n b: ReputationSubject,\n amount: string,\n atMs: number,\n ): void {\n for (const [subject, other] of [\n [a, b],\n [b, a],\n ] as const) {\n const ev = this.touch(subject, atMs);\n ev.settled += 1;\n ev.volume = sumDecimal([ev.volume, amount]);\n const key = subjectKey(other);\n const line = ev.counterparties.get(key) ?? { settled: 0, volume: '0' };\n line.settled += 1;\n line.volume = sumDecimal([line.volume, amount]);\n ev.counterparties.set(key, line);\n }\n }\n\n recordRefusal(subject: ReputationSubject, code: string, atMs: number): void {\n const ev = this.touch(subject, atMs);\n ev.refusals[code] = (ev.refusals[code] ?? 0) + 1;\n }\n\n recordShadowSpend(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).shadowSpends += 1;\n }\n\n recordDispute(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).disputes += 1;\n }\n\n recordEndorsement(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).endorsements += 1;\n }\n\n merge(canonical: ReputationSubject, alias: ReputationSubject): void {\n const aliasKey = subjectKey(alias);\n const canonicalKey = subjectKey(canonical);\n const from = this.records.get(aliasKey);\n if (!from || aliasKey === canonicalKey) return;\n // The canonical side may have no record yet — the alias's history becomes\n // its history (same create shape as touch, seeded with the alias's window).\n const into = this.records.get(canonicalKey) ?? this.touch(canonical, from.firstSeenMs);\n into.firstSeenMs = Math.min(into.firstSeenMs, from.firstSeenMs);\n into.lastSeenMs = Math.max(into.lastSeenMs, from.lastSeenMs);\n into.attempts += from.attempts;\n into.settled += from.settled;\n into.volume = sumDecimal([into.volume, from.volume]);\n for (const [code, count] of Object.entries(from.refusals)) {\n into.refusals[code] = (into.refusals[code] ?? 0) + count;\n }\n into.shadowSpends += from.shadowSpends;\n into.disputes += from.disputes;\n into.endorsements += from.endorsements;\n for (const [peerKey, line] of from.counterparties) {\n if (peerKey === canonicalKey) continue; // a self-edge would score the subject by itself\n const existing = into.counterparties.get(peerKey) ?? { settled: 0, volume: '0' };\n existing.settled += line.settled;\n existing.volume = sumDecimal([existing.volume, line.volume]);\n into.counterparties.set(peerKey, existing);\n // Re-key the peer's reverse edge: alias -> canonical.\n const peer = this.records.get(peerKey);\n const held = peer?.counterparties.get(aliasKey);\n if (peer && held) {\n peer.counterparties.delete(aliasKey);\n const reverse = peer.counterparties.get(canonicalKey) ?? { settled: 0, volume: '0' };\n reverse.settled += held.settled;\n reverse.volume = sumDecimal([reverse.volume, held.volume]);\n peer.counterparties.set(canonicalKey, reverse);\n }\n }\n into.counterparties.delete(aliasKey);\n this.records.delete(aliasKey);\n }\n\n get(subject: ReputationSubject): SubjectEvidence | undefined {\n return this.records.get(subjectKey(subject));\n }\n\n getByKey(key: string): SubjectEvidence | undefined {\n return this.records.get(key);\n }\n\n all(): IterableIterator<SubjectEvidence> {\n return this.records.values();\n }\n\n get size(): number {\n return this.records.size;\n }\n\n /**\n * Insert a fully-formed record verbatim — the hydration primitive a durable\n * store uses to rebuild the working set on open. Bypasses the live counters\n * (the record already holds its accumulated state and timestamps).\n */\n load(record: SubjectEvidence): void {\n this.records.set(subjectKey(record.subject), record);\n }\n\n /** Get-or-create the record for a subject and stamp first/last seen. */\n private touch(subject: ReputationSubject, atMs: number): SubjectEvidence {\n const key = subjectKey(subject);\n let record = this.records.get(key);\n if (!record) {\n record = {\n subject: normalizeSubject(subject),\n firstSeenMs: atMs,\n lastSeenMs: atMs,\n attempts: 0,\n settled: 0,\n volume: '0',\n refusals: {},\n shadowSpends: 0,\n disputes: 0,\n endorsements: 0,\n counterparties: new Map(),\n };\n this.records.set(key, record);\n }\n if (atMs < record.firstSeenMs) record.firstSeenMs = atMs;\n if (atMs > record.lastSeenMs) record.lastSeenMs = atMs;\n return record;\n }\n}\n","import type { MaybePromise } from './evidence.js';\n\n/**\n * What an `intent.created` event tells us, kept until the matching\n * `payment.settled` (which carries only an intentId) can be attributed.\n */\nexport interface IntentFacts {\n agentId: string;\n host: string;\n amount: string;\n}\n\nexport const DEFAULT_CORRELATION_LIMIT = 10_000;\n\n/**\n * Bridges `intent.created` -> later `payment.settled`/`decision.made`. A\n * `payment.settled` event names only an intentId, so the graph remembers every\n * undecided intent until it settles. Bounded (oldest-evicted) so undecided\n * intents that never settle cannot leak. Writes may be async (a durable store\n * persists them so an in-flight intent survives a restart); reads are sync.\n */\nexport interface IntentCorrelationPort {\n remember(intentId: string, facts: IntentFacts): MaybePromise<void>;\n /** Read the facts without consuming them (decision.made reads, doesn't burn). */\n peek(intentId: string): IntentFacts | undefined;\n /** Read and consume (payment.settled attributes once, then forgets). */\n take(intentId: string): IntentFacts | undefined;\n readonly size: number;\n /**\n * Await any pending durable writes (no-op in memory). Durable\n * implementations surface write failures HERE — the graph fire-and-forgets\n * its writes, so flush() is the error channel.\n */\n flush?(): Promise<void>;\n}\n\n/** In-memory correlation map with FIFO eviction once `limit` is reached. */\nexport class InMemoryIntentStore implements IntentCorrelationPort {\n private readonly intents = new Map<string, IntentFacts>();\n\n constructor(private readonly limit: number = DEFAULT_CORRELATION_LIMIT) {}\n\n remember(intentId: string, facts: IntentFacts): void {\n // Re-remembering an existing id is an update, not a new entry — evicting\n // for it would shrink the working set below the bound (and desync a\n // durable mirror, whose upsert adds no row). Updates keep their position,\n // matching an SQL upsert leaving seq unchanged.\n if (!this.intents.has(intentId) && this.intents.size >= this.limit) {\n const oldest = this.intents.keys().next().value;\n if (oldest !== undefined) this.intents.delete(oldest);\n }\n this.intents.set(intentId, facts);\n }\n\n peek(intentId: string): IntentFacts | undefined {\n return this.intents.get(intentId);\n }\n\n take(intentId: string): IntentFacts | undefined {\n const facts = this.intents.get(intentId);\n if (facts) this.intents.delete(intentId);\n return facts;\n }\n\n get size(): number {\n return this.intents.size;\n }\n}\n","import type { ReputationComponents } from '@reinconsole/core';\nimport type { SubjectEvidence } from './evidence.js';\n\n/**\n * Component weights, summing to 1. The blend is a deliberately transparent\n * heuristic: every component is 0–100 with higher = healthier, so a score is\n * readable straight off its explanation. Sophistication can replace this\n * later without touching the evidence model — scores are pure functions of\n * evidence, never stored.\n */\nexport interface ScoreWeights {\n volume: number;\n longevity: number;\n disputeRate: number;\n counterpartyQuality: number;\n settlementReliability: number;\n}\n\nexport const DEFAULT_WEIGHTS: ScoreWeights = {\n settlementReliability: 0.3,\n disputeRate: 0.3,\n volume: 0.15,\n counterpartyQuality: 0.15,\n longevity: 0.1,\n};\n\nconst DAY_MS = 86_400_000;\n\n/**\n * How much one bad observation hurts, relative to a plain refusal. Disputes\n * and shadow spends are deliberate misconduct; a replay means someone tried\n * to spend the same money twice; the rest is sloppiness.\n */\nconst BADNESS = { dispute: 4, shadowSpend: 4, replay: 3, refusal: 1, endorsementCredit: 2 };\n\nconst REPLAY_CODES = new Set(['payment_replayed', 'decision_replayed']);\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.min(hi, Math.max(lo, value));\n}\n\nfunction refusalCounts(ev: SubjectEvidence): { replays: number; others: number } {\n let replays = 0;\n let others = 0;\n for (const [code, count] of Object.entries(ev.refusals)) {\n if (REPLAY_CODES.has(code)) replays += count;\n else others += count;\n }\n return { replays, others };\n}\n\n/** Saturating settled-transaction volume: 0 at none, ~63 at 20, ~92 at 50. */\nexport function volumeComponent(ev: SubjectEvidence): number {\n return 100 * (1 - Math.exp(-ev.settled / 20));\n}\n\n/** How long the subject has been known: linear to 100 at 30 days. */\nexport function longevityComponent(ev: SubjectEvidence, nowMs: number): number {\n const knownDays = Math.max(0, nowMs - ev.firstSeenMs) / DAY_MS;\n return 100 * Math.min(1, knownDays / 30);\n}\n\n/**\n * Dispute hygiene (the core field is named `disputeRate`; the component is\n * stored INVERTED — 100 = a clean record — so all five components blend in\n * the same direction). Weighted badness over total interactions; manual\n * endorsements buy back a little slack.\n */\nexport function disputeComponent(ev: SubjectEvidence): number {\n const { replays, others } = refusalCounts(ev);\n const badness = Math.max(\n 0,\n ev.disputes * BADNESS.dispute +\n ev.shadowSpends * BADNESS.shadowSpend +\n replays * BADNESS.replay +\n others * BADNESS.refusal -\n ev.endorsements * BADNESS.endorsementCredit,\n );\n const interactions = Math.max(1, ev.attempts + ev.disputes + ev.endorsements);\n return 100 * Math.max(0, 1 - badness / interactions);\n}\n\n/** Settled / attempted. No attempts yet = neutral 50, not perfect 100. */\nexport function reliabilityComponent(ev: SubjectEvidence): number {\n if (ev.attempts === 0) return 50;\n return 100 * Math.min(1, ev.settled / ev.attempts);\n}\n\n/**\n * Confidence is first-class (see core ReputationScore): a thin or brand-new\n * history yields LOW confidence rather than a misleading score, so consumers\n * (engine sync, gate screening) can refuse to act on it. Depth of evidence\n * saturates around 10 observations; age stops mattering after a week.\n *\n * The 0.1 age floor is load-bearing for fairness. Both consumers act at a 0.3\n * confidence floor (`syncVendors`, `payerCheck`), so capping a day-0 subject at\n * 0.1 makes first-day enforcement impossible BY CONSTRUCTION rather than by\n * arithmetic luck. It used to be 0.4, which left day-0 subjects enforceable\n * after only 14 observations — `depth` saturates so fast that a busy vendor\n * cleared that in seconds, and the console's own vendor sat at 0.30136 against\n * the 0.3 floor, i.e. inside the newcomer grace period by 0.0014. Enforcement\n * now needs roughly 1.6 days (high volume) to 2.3 days (moderate) of history:\n * a grace period measured in time, which is what \"newcomer\" means.\n *\n * Note the constant only ever applies to subjects younger than a week — past\n * 7 days `min(1, d/7)` saturates and age is 1.0 under any floor — so lowering\n * it leaves every mature score bit-for-bit unchanged.\n */\nexport function confidence(ev: SubjectEvidence, nowMs: number): number {\n const { replays, others } = refusalCounts(ev);\n const observations =\n ev.attempts + ev.disputes + ev.endorsements + ev.shadowSpends + replays + others;\n const depth = 1 - Math.exp(-observations / 10);\n const knownDays = Math.max(0, nowMs - ev.firstSeenMs) / DAY_MS;\n const age = 0.1 + 0.9 * Math.min(1, knownDays / 7);\n return clamp(depth * age, 0, 1);\n}\n\n/** Blend all five components into the headline 0–100 score. */\nexport function blend(components: ReputationComponents, weights: ScoreWeights): number {\n const total =\n components.volume * weights.volume +\n components.longevity * weights.longevity +\n components.disputeRate * weights.disputeRate +\n components.counterpartyQuality * weights.counterpartyQuality +\n components.settlementReliability * weights.settlementReliability;\n return clamp(Math.round(total), 0, 100);\n}\n\n/**\n * The one-hop base score: the blend WITHOUT counterpartyQuality, with the\n * remaining weights renormalized. Counterparty quality for subject A averages\n * the base scores of A's counterparties — base, not full, so the computation\n * is a single hop and cycles (A pays B pays A) cannot recurse.\n */\nexport function blendBase(ev: SubjectEvidence, nowMs: number, weights: ScoreWeights): number {\n const remaining =\n weights.volume + weights.longevity + weights.disputeRate + weights.settlementReliability;\n if (remaining <= 0) return 50;\n const total =\n volumeComponent(ev) * weights.volume +\n longevityComponent(ev, nowMs) * weights.longevity +\n disputeComponent(ev) * weights.disputeRate +\n reliabilityComponent(ev) * weights.settlementReliability;\n return clamp(total / remaining, 0, 100);\n}\n","import {\n ReputationScore,\n type Receipt,\n type ReinEvent,\n type ReputationComponents,\n type ReputationSubject,\n} from '@reinconsole/core';\nimport {\n EvidenceLedger,\n normalizeSubject,\n subjectKey,\n type EvidenceLedgerPort,\n type MaybePromise,\n type SubjectEvidence,\n} from './evidence.js';\nimport {\n DEFAULT_CORRELATION_LIMIT,\n InMemoryIntentStore,\n type IntentCorrelationPort,\n} from './intents.js';\nimport {\n blend,\n blendBase,\n confidence,\n DEFAULT_WEIGHTS,\n disputeComponent,\n longevityComponent,\n reliabilityComponent,\n volumeComponent,\n type ScoreWeights,\n} from './scoring.js';\n\n/** Anything with a Rein event bus: engine, indexer, gate, signer. */\nexport interface EventSource {\n onEvent(handler: (event: ReinEvent) => void): void;\n}\n\n/**\n * Gate refusal codes that are the vendor's throttle or the vendor's rails\n * failing — not payer misbehavior. They must never become reputation evidence\n * (see the `gate.refused` case below). Mirrors @reinconsole/gate's no-fault classes;\n * kept as strings because the event schema deliberately carries codes as\n * strings (remote gates may run other versions).\n */\nconst NO_FAULT_GATE_CODES = new Set([\n 'rate_limited',\n 'velocity_exceeded',\n 'rails_unavailable',\n 'settle_unknown',\n 'price_unavailable',\n 'price_ceiling',\n]);\n\n/**\n * Where vendor scores land. `PolicyEngine.spend` satisfies this structurally,\n * so `graph.syncVendors(engine.spend)` closes the loop — durable when the\n * engine runs on @reinconsole/store.\n */\nexport interface VendorReputationSink {\n setVendorReputation(host: string, score: number): void | Promise<void>;\n}\n\nexport interface ReputationGraphOptions {\n weights?: Partial<ScoreWeights>;\n /** Injectable clock (scores are functions of evidence AND time). */\n now?: () => Date;\n /**\n * Max undecided intents remembered for settlement correlation. Ignored when\n * a custom `intents` store is injected (that store owns its own bound).\n */\n correlationLimit?: number;\n /**\n * Durable evidence ledger. Defaults to in-memory; @reinconsole/store provides a\n * PGlite-backed one so scores survive a restart.\n */\n ledger?: EvidenceLedgerPort;\n /** Durable intent correlation store. Defaults to in-memory (bounded by `correlationLimit`). */\n intents?: IntentCorrelationPort;\n}\n\nexport interface ManualReport {\n subject: ReputationSubject;\n kind: 'dispute' | 'endorsement';\n /** When the incident happened (defaults to now). */\n at?: Date;\n note?: string;\n}\n\nexport interface SyncedVendorScore {\n host: string;\n score: number;\n confidence: number;\n}\n\nexport interface SyncOptions {\n /**\n * Scores below this confidence are NOT pushed — the evaluator treats an\n * unknown reputation as indeterminate (never triggers vendorReputationLt),\n * and a thin history must stay unknown rather than condemn a newcomer.\n */\n minConfidence?: number;\n}\n\n/** A score plus the raw evidence it was derived from, render-ready. */\nexport interface ReputationExplanation {\n score: ReputationScore;\n weights: ScoreWeights;\n evidence: {\n attempts: number;\n settled: number;\n volume: string;\n refusals: Record<string, number>;\n shadowSpends: number;\n disputes: number;\n endorsements: number;\n firstSeen: Date;\n lastSeen: Date;\n counterparties: { subject: ReputationSubject; settled: number; volume: string }[];\n };\n}\n\n/**\n * The Rein reputation graph (Phase 3). Subscribes to the event buses both\n * sides already publish — the engine/indexer name vendors by host and agents\n * by id; a gate names payers by wallet — and accumulates per-subject evidence:\n * settlements, refusals, shadow spends, manual disputes, and settled-money\n * edges between counterparties. Scores are computed on demand (never stored)\n * from that evidence, so every number is explainable, and they feed back into\n * enforcement on both sides of the wire:\n *\n * graph.syncVendors(engine.spend) -> policies with vendorReputationLt fire\n * screen: { check: payerCheck(graph) } -> a gate turns away low-rep wallets\n *\n * Event mapping notes: engine-side evidence keys agents by ULID and vendors by\n * host; gate-side evidence keys payers by wallet and recipients by payTo\n * address — distinct subjects, so feeding one graph from BOTH buses (the\n * console world) never double-counts. `payment.settled` carries only an\n * intentId, so the graph keeps a bounded intent.created correlation map.\n * Denied decisions are deliberately NOT held against anyone: a deny means\n * policy worked, not that the agent or vendor misbehaved.\n */\nexport class ReputationGraph {\n private readonly ledger: EvidenceLedgerPort;\n private readonly intents: IntentCorrelationPort;\n private readonly weights: ScoreWeights;\n private readonly now: () => Date;\n /** aliasKey -> canonical subject. Links are derived state (an agent registry\n * or ERC-8004 lookup knows them) — re-assert at boot; merges are idempotent. */\n private readonly aliases = new Map<string, ReputationSubject>();\n\n constructor(options: ReputationGraphOptions = {}) {\n this.weights = { ...DEFAULT_WEIGHTS, ...options.weights };\n this.now = options.now ?? (() => new Date());\n this.ledger = options.ledger ?? new EvidenceLedger();\n this.intents =\n options.intents ??\n new InMemoryIntentStore(options.correlationLimit ?? DEFAULT_CORRELATION_LIMIT);\n }\n\n /** Subscribe to a bus. Returns `this` so construction chains. */\n observe(source: EventSource): this {\n source.onEvent((event) => this.ingest(event));\n return this;\n }\n\n /**\n * Fire-and-forget a port write: bus handlers cannot await, and a rejecting\n * durable write must never become an unhandled rejection (Node kills the\n * process). Failures are the port's to surface — flush() throws them.\n */\n private fire(write: () => MaybePromise<void>): void {\n // Thunk form (parity with the gate's fire, S19): a ledger that THROWS\n // synchronously must not turn an ingest into a crash — telemetry failures\n // are the port's to surface, and flush() throws them.\n try {\n void Promise.resolve(write()).catch(() => undefined);\n } catch {\n // swallowed by design — see above\n }\n }\n\n /**\n * Declare that `alias` is the same real-world party as `canonical` (the\n * ERC-8004 identity story: one identity, many addresses/ids — an engine\n * agent's ULID and its paying wallet, or a vendor's host and its payTo\n * address). Everything already known about the alias is folded into the\n * canonical subject, and all future evidence and lookups for the alias\n * resolve to it. Idempotent — re-asserting known links at boot is free.\n */\n link(canonical: ReputationSubject, alias: ReputationSubject): void {\n const target = normalizeSubject(this.resolve(canonical)); // flatten chains\n const aliasKey = subjectKey(alias);\n if (subjectKey(target) === aliasKey) return;\n this.aliases.set(aliasKey, target);\n // Repoint any alias that resolved THROUGH this alias (a->b then b->c).\n for (const [key, resolved] of this.aliases) {\n if (subjectKey(resolved) === aliasKey) this.aliases.set(key, target);\n }\n this.fire(() => this.ledger.merge(target, alias));\n }\n\n /** The canonical subject for a possibly-aliased one. */\n private resolve(subject: ReputationSubject): ReputationSubject {\n return this.aliases.get(subjectKey(subject)) ?? subject;\n }\n\n ingest(event: ReinEvent): void {\n const atMs = event.at.getTime();\n switch (event.type) {\n case 'intent.created': {\n this.fire(() =>\n this.intents.remember(event.intent.id, {\n agentId: event.intent.agentId,\n host: event.intent.vendor.host,\n amount: event.intent.amount,\n }),\n );\n return;\n }\n case 'decision.made': {\n if (event.decision.outcome !== 'allow') return;\n const facts = this.intents.peek(event.decision.intentId);\n if (!facts) return;\n this.fire(() => this.ledger.recordAttempt(this.resolve({ kind: 'agent', id: facts.agentId }), atMs));\n this.fire(() => this.ledger.recordAttempt(this.resolve({ kind: 'vendor', id: facts.host }), atMs));\n return;\n }\n case 'payment.settled': {\n const facts = this.intents.take(event.payment.intentId);\n if (!facts) return; // unattributable — no subject to credit\n const agent = this.resolve({ kind: 'agent', id: facts.agentId });\n const vendor = this.resolve({ kind: 'vendor', id: facts.host });\n this.fire(() => this.ledger.recordSettlement(agent, vendor, facts.amount, atMs));\n return;\n }\n case 'shadow.spend': {\n this.fire(() => this.ledger.recordShadowSpend(this.resolve({ kind: 'agent', id: event.agentId }), atMs));\n return;\n }\n case 'signature.refused': {\n const agentId = event.agentId; // hoisted: narrowing must survive the thunk\n if (!agentId) return;\n this.fire(() =>\n this.ledger.recordRefusal(this.resolve({ kind: 'agent', id: agentId }), event.code, atMs),\n );\n return;\n }\n case 'gate.settled': {\n const payer = this.resolve({ kind: 'agent', id: event.receipt.payer });\n const recipient = this.resolve({ kind: 'vendor', id: event.receipt.payTo });\n this.fire(() => this.ledger.recordAttempt(payer, atMs));\n this.fire(() => this.ledger.recordAttempt(recipient, atMs));\n this.fire(() => this.ledger.recordSettlement(payer, recipient, event.receipt.amount, atMs));\n return;\n }\n case 'gate.refused': {\n // No-fault refusals carry NO evidence — the gate-side parallel of\n // \"denied decisions count against no one\". Throttle codes are the\n // VENDOR'S cap (one gate's rate limit must not bleed into a payer's\n // global score); rails codes are the vendor's infrastructure failing\n // (a settle_unknown payment may even have gone through). Not even the\n // attempt is recorded: an attempt with no settlement would silently\n // drag settlementReliability down.\n if (!event.payer || NO_FAULT_GATE_CODES.has(event.code)) return;\n const payer = this.resolve({ kind: 'agent', id: event.payer });\n this.fire(() => this.ledger.recordAttempt(payer, atMs));\n this.fire(() => this.ledger.recordRefusal(payer, event.code, atMs));\n return;\n }\n // signature.released and gate.quoted carry no evidence the engine-side\n // events don't already: released duplicates the allow that preceded it,\n // and a quote names no payer.\n case 'signature.released':\n case 'gate.quoted':\n return;\n }\n }\n\n /**\n * Agent-side alternative to observing the engine bus: feed the SDK guard's\n * onReceipt straight in. Connect events OR receipts per side, not both —\n * a receipt restates the decision/settlement the bus already carried.\n */\n ingestReceipt(receipt: Receipt): void {\n if (receipt.outcome !== 'allow') return; // policy verdicts are not subject badness\n const atMs = receipt.createdAt.getTime();\n const agent = this.resolve({ kind: 'agent', id: receipt.agentId });\n const vendor = this.resolve({ kind: 'vendor', id: receipt.vendorHost });\n this.fire(() => this.ledger.recordAttempt(agent, atMs));\n this.fire(() => this.ledger.recordAttempt(vendor, atMs));\n if (receipt.settlement?.txHash) {\n this.fire(() => this.ledger.recordSettlement(agent, vendor, receipt.amount, atMs));\n }\n }\n\n /** Record an out-of-band dispute or endorsement against a subject. */\n report(input: ManualReport): void {\n const atMs = (input.at ?? this.now()).getTime();\n const subject = this.resolve(input.subject);\n if (input.kind === 'dispute') this.fire(() => this.ledger.recordDispute(subject, atMs));\n else this.fire(() => this.ledger.recordEndorsement(subject, atMs));\n }\n\n /** Await any pending durable writes (no-op for in-memory stores). */\n async flush(): Promise<void> {\n await this.ledger.flush?.();\n await this.intents.flush?.();\n }\n\n /** The score for one subject, or undefined when nothing is known (fairness).\n * Aliased subjects resolve to their canonical identity — a linked wallet\n * answers with the merged history, on both sides of the wire. */\n score(subject: ReputationSubject): ReputationScore | undefined {\n const ev = this.ledger.get(this.resolve(subject));\n return ev && this.scoreOf(ev);\n }\n\n /** All known scores, best first. */\n scores(kind?: ReputationSubject['kind']): ReputationScore[] {\n const out: ReputationScore[] = [];\n for (const ev of this.ledger.all()) {\n if (kind && ev.subject.kind !== kind) continue;\n out.push(this.scoreOf(ev));\n }\n return out.sort((a, b) => b.score - a.score);\n }\n\n /** The score plus the raw evidence behind it. */\n explain(subject: ReputationSubject): ReputationExplanation | undefined {\n const ev = this.ledger.get(this.resolve(subject));\n if (!ev) return undefined;\n return {\n score: this.scoreOf(ev),\n weights: this.weights,\n evidence: {\n attempts: ev.attempts,\n settled: ev.settled,\n volume: ev.volume,\n refusals: { ...ev.refusals },\n shadowSpends: ev.shadowSpends,\n disputes: ev.disputes,\n endorsements: ev.endorsements,\n firstSeen: new Date(ev.firstSeenMs),\n lastSeen: new Date(ev.lastSeenMs),\n counterparties: [...ev.counterparties.entries()].map(([key, line]) => {\n const [kind, ...id] = key.split(':');\n return {\n subject: { kind: kind as ReputationSubject['kind'], id: id.join(':') },\n settled: line.settled,\n volume: line.volume,\n };\n }),\n },\n };\n }\n\n /**\n * Push every vendor score that clears the confidence floor into the engine's\n * spend store (or any sink). Returns what was pushed. Low-confidence scores\n * are withheld so `vendorReputationLt` keeps treating thin histories as\n * unknown — the evaluator's no-data-no-trigger rule stays intact end to end.\n */\n async syncVendors(\n sink: VendorReputationSink,\n options: SyncOptions = {},\n ): Promise<SyncedVendorScore[]> {\n const minConfidence = options.minConfidence ?? 0.3;\n const pushed: SyncedVendorScore[] = [];\n for (const ev of this.ledger.all()) {\n if (ev.subject.kind !== 'vendor') continue;\n const score = this.scoreOf(ev);\n if (score.confidence < minConfidence) continue;\n await sink.setVendorReputation(ev.subject.id, score.score);\n pushed.push({ host: ev.subject.id, score: score.score, confidence: score.confidence });\n }\n return pushed;\n }\n\n /** Number of subjects with evidence. */\n subjects(): number {\n return this.ledger.size;\n }\n\n private scoreOf(ev: SubjectEvidence): ReputationScore {\n const nowMs = this.now().getTime();\n const components: ReputationComponents = {\n volume: volumeComponent(ev),\n longevity: longevityComponent(ev, nowMs),\n disputeRate: disputeComponent(ev),\n counterpartyQuality: this.counterpartyQuality(ev, nowMs),\n settlementReliability: reliabilityComponent(ev),\n };\n return ReputationScore.parse({\n subject: ev.subject,\n score: blend(components, this.weights),\n components,\n confidence: confidence(ev, nowMs),\n asOf: new Date(nowMs),\n });\n }\n\n /** One-hop mean of the counterparties' base scores; neutral 50 when alone. */\n private counterpartyQuality(ev: SubjectEvidence, nowMs: number): number {\n if (ev.counterparties.size === 0) return 50;\n let total = 0;\n for (const key of ev.counterparties.keys()) {\n const other = this.ledger.getByKey(key);\n total += other ? blendBase(other, nowMs, this.weights) : 50;\n }\n return total / ev.counterparties.size;\n }\n}\n\nexport interface PayerCheckOptions {\n /** Refuse payers scoring below this (default 40). */\n denyBelow?: number;\n /** Ignore scores below this confidence (default 0.3) — newcomers pass. */\n minConfidence?: number;\n}\n\n/**\n * Reputation-driven gate screening: plugs into @reinconsole/gate's `screen.check`.\n * Unknown wallets and thin histories pass (same fairness rule as the engine\n * sync); a confident low score is turned away at the door, before any\n * facilitator round-trip.\n */\nexport function payerCheck(\n graph: ReputationGraph,\n options: PayerCheckOptions = {},\n): (payer: string) => string | undefined {\n const denyBelow = options.denyBelow ?? 40;\n const minConfidence = options.minConfidence ?? 0.3;\n return (payer) => {\n const score = graph.score({ kind: 'agent', id: payer });\n if (!score || score.confidence < minConfidence) return undefined;\n if (score.score >= denyBelow) return undefined;\n return `payer reputation ${score.score} is below this gate's floor of ${denyBelow}`;\n };\n}\n","import { fileURLToPath } from 'node:url';\nimport { realpathSync } from 'node:fs';\nimport Fastify, { type FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport { ReinEvent, ReputationSubject } from '@reinconsole/core';\nimport { ApiKeyAuth, AuthError } from '@reinconsole/core/auth';\nimport { ReputationGraph } from './graph.js';\n\nconst ReportInput = z.object({\n subject: ReputationSubject,\n kind: z.enum(['dispute', 'endorsement']),\n at: z.coerce.date().optional(),\n note: z.string().max(500).optional(),\n});\n\nconst ScoresQuery = z.object({ kind: z.enum(['agent', 'vendor']).optional() });\n\nconst LinkInput = z\n .object({\n canonical: ReputationSubject,\n alias: ReputationSubject,\n })\n // Merging across kinds would delete a vendor row into an agent identity\n // (silently dropping the host from syncVendors) — always a caller bug.\n .refine((l) => l.canonical.kind === l.alias.kind, {\n message: 'canonical and alias must be the same kind',\n });\n\nexport interface GraphServerOptions {\n /**\n * API-key authentication for the WRITE routes. Omit for embedded use (the\n * console world holds the graph object directly) or for a loopback-only\n * process; pass an {@link ApiKeyAuth} and every write demands a `report`\n * scope while reads stay open.\n */\n auth?: ApiKeyAuth;\n}\n\n/** Writes accept evidence; reads only reflect it back. */\nfunction isWrite(method: string): boolean {\n return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS';\n}\n\n/**\n * Build the graph HTTP API. Remote producers POST their events here; anyone\n * can read scores with the evidence behind them. Pass a graph for tests, or\n * let it create a fresh in-memory one.\n *\n * READS ARE OPEN BY DESIGN and that is the whole point of a reputation graph:\n * a score nobody can read governs nothing. WRITES are the asymmetry — an event\n * posted here becomes evidence about a subject that did not post it — so when\n * `auth` is supplied they require the `report` scope. Without `auth` the server\n * is exactly as open as it always was, which is why the bins that expose it\n * refuse a public bind unless a key exists or the operator says otherwise.\n */\nexport function buildGraphServer(\n graph: ReputationGraph = new ReputationGraph(),\n options: GraphServerOptions = {},\n): FastifyInstance {\n const app = Fastify({ logger: false });\n const auth = options.auth;\n\n // Turn ZodErrors into clean 400s instead of 500s.\n app.setErrorHandler((err: unknown, _req, reply) => {\n if (err instanceof z.ZodError) {\n return reply.status(400).send({ error: 'validation_error', issues: err.issues });\n }\n if (AuthError.is(err)) {\n return reply.status(err.status).send({ error: err.code, message: err.message });\n }\n const message = err instanceof Error ? err.message : String(err);\n return reply.status(500).send({ error: 'internal_error', message });\n });\n\n // Auth runs before routing, so an unknown write path cannot leak whether it\n // exists. A missing credential is a 401 with a challenge, never a silent\n // pass and never a 404 pretending the route moved.\n if (auth) {\n app.addHook('onRequest', async (req, reply) => {\n if (!isWrite(req.method)) return;\n try {\n auth.authenticate(req.headers, 'report');\n } catch (err) {\n if (AuthError.is(err)) {\n if (err.status === 401) reply.header('WWW-Authenticate', 'Bearer realm=\"rein-graph\"');\n return reply.status(err.status).send({ error: err.code, message: err.message });\n }\n throw err;\n }\n return;\n });\n }\n\n app.get('/health', () => ({\n status: 'ok',\n subjects: graph.subjects(),\n // Advertised so a producer can tell \"this graph wants a key\" apart from\n // \"my key is wrong\" without guessing from a 401.\n auth: auth ? 'api-key' : 'none',\n }));\n\n // --- Ingestion ---\n // The HTTP path can await durability even though the in-process bus path\n // cannot — flush the durable writes before answering so a POST that returns\n // 200 is persisted.\n app.post('/v1/events', async (req) => {\n const events = z.union([ReinEvent.transform((e) => [e]), z.array(ReinEvent)]).parse(req.body);\n for (const event of events) graph.ingest(event);\n await graph.flush();\n return { ingested: events.length };\n });\n\n app.post('/v1/reports', async (req) => {\n const input = ReportInput.parse(req.body);\n graph.report(input);\n await graph.flush();\n return graph.score(input.subject);\n });\n\n // --- Identity links ---\n // Unverified by DESIGN: this endpoint's trust level equals POST /v1/events —\n // whoever can post events can already fabricate the evidence itself. On-chain\n // verification belongs to the CALLER (@reinconsole/erc8004 derives link facts from\n // the Identity Registry, then asserts them here).\n app.post('/v1/links', async (req) => {\n const links = z.union([LinkInput.transform((l) => [l]), z.array(LinkInput)]).parse(req.body);\n for (const link of links) graph.link(link.canonical, link.alias);\n await graph.flush(); // durable merges land before the 200 (same contract as /v1/events)\n return { linked: links.length };\n });\n\n // --- Scores ---\n app.get('/v1/scores', (req) => {\n const { kind } = ScoresQuery.parse(req.query);\n return graph.scores(kind);\n });\n\n app.get('/v1/scores/:kind/:id', (req, reply) => {\n const subject = ReputationSubject.parse(req.params);\n const explanation = graph.explain(subject);\n if (!explanation) {\n return reply\n .status(404)\n .send({ error: 'not_found', message: `no evidence for ${subject.kind} ${subject.id}` });\n }\n return explanation;\n });\n\n return app;\n}\n\nconst LOOPBACK = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);\n\n/**\n * Build the graph's auth layer from the environment, or explain why there is\n * none. `REIN_GRAPH_API_KEY` seeds one or more comma-separated secrets.\n *\n * They are seeded with `report` and nothing else: the graph has no key-issuing\n * routes to bootstrap (unlike the engine, which seeds `admin` so `/v1/keys` is\n * reachable), so an `admin` secret here would grant authority no route needs.\n */\nexport async function graphAuthFromEnv(env: NodeJS.ProcessEnv): Promise<ApiKeyAuth | undefined> {\n const raw = env['REIN_GRAPH_API_KEY']?.trim();\n if (!raw) return undefined;\n const auth = new ApiKeyAuth();\n const secrets = raw\n .split(',')\n .map((secret) => secret.trim())\n .filter(Boolean);\n for (const [i, secret] of secrets.entries()) {\n await auth.issue({ name: `env-key-${i + 1}`, scopes: ['report'], secret });\n }\n return auth.hasKeys() ? auth : undefined;\n}\n\n/**\n * Where a graph process binds, given whether auth exists.\n *\n * Until D1(a) the graph had no key to trade, so the only trade available was a\n * statement: `REIN_GRAPH_PUBLIC=1`. Now that writes can demand a `report` key,\n * the rule matches the engine's — a key buys a public bind, and asking for one\n * without a key is a startup ERROR rather than a warning. `REIN_GRAPH_PUBLIC=1`\n * survives as the deliberate override for an open graph, because a read-only\n * showcase is a real deployment and reads were never the exposure.\n */\nexport function resolveGraphHost(\n env: NodeJS.ProcessEnv,\n hasAuth = false,\n): { host: string; warning?: string } {\n const requested = env['HOST']?.trim();\n const optedIn = env['REIN_GRAPH_PUBLIC']?.trim() === '1';\n if (hasAuth) return { host: requested || '0.0.0.0' };\n if (optedIn) {\n return {\n host: requested || '0.0.0.0',\n warning:\n 'REIN_GRAPH_PUBLIC=1 — this reputation graph is answering unauthenticated requests. ' +\n 'Anyone who can reach it can write evidence that moves scores.',\n };\n }\n if (requested && !LOOPBACK.has(requested)) {\n throw new Error(\n `refusing to bind ${requested}: the reputation graph has no authentication.` +\n '\\n Set REIN_GRAPH_API_KEY=<secret> to protect its writes, or' +\n '\\n set REIN_GRAPH_PUBLIC=1 to expose an open graph deliberately.',\n );\n }\n return { host: requested || '127.0.0.1' };\n}\n\n// Start the server when run directly (tsx/node), not when imported.\nfunction isMainModule(): boolean {\n if (!process.argv[1]) return false;\n try {\n return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n const port = Number(process.env.PORT ?? 8788);\n // This block used to default to 0.0.0.0 with no auth at all — the in-memory\n // twin of the exposure D1 closed on the durable bin, and the more reachable\n // of the two, since it needs no data directory to run.\n graphAuthFromEnv(process.env)\n .then((auth) => {\n const { host, warning } = resolveGraphHost(process.env, auth !== undefined);\n if (warning) console.warn(`[rein] ${warning}`);\n const app = buildGraphServer(new ReputationGraph(), { ...(auth ? { auth } : {}) });\n return app\n .listen({ port, host })\n .then(() =>\n console.log(\n `[rein] graph listening on http://${host}:${port} (auth: ${auth ? 'api-key' : 'none'})`,\n ),\n );\n })\n .catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n","import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { ApiKeyRecord, toPublicApiKey, type ApiKey, type ApiKeyScope } from './api-key.js';\nimport { newId } from './ulid.js';\n\n/**\n * API-key authentication for a Rein service's HTTP surface.\n *\n * Secrets are never stored: a service keeps sha256 digests, so a leaked\n * database yields nothing that authenticates. A secret is returned exactly\n * once, at issuance. Rotation mints a new secret while keeping the outgoing\n * digest valid for a grace window, so a fleet rolls over without a flag-day\n * restart.\n *\n * This lives in core, beside the {@link ApiKeyRecord} schema it reads and\n * writes, because three services need the SAME answer to \"who is calling and\n * may they do this\" — the engine, the signer's admin surface, and the graph's\n * ingestion routes. Two implementations of authentication is two things to get\n * wrong. It is reached as `@reinconsole/core/auth` rather than through the\n * package barrel ON PURPOSE: the barrel is imported by the console's BROWSER\n * bundle, and every symbol below needs `node:crypto`.\n */\n\n/** A port may answer synchronously (in memory) or not (durable). */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** The presented-secret prefix, so a leaked key is greppable in logs and repos. */\nconst SECRET_PREFIX = 'rk_';\n\n/** How often a key's `lastUsedAt` is written through. Telemetry, not authority. */\nconst TOUCH_INTERVAL_MS = 60_000;\n\n/** Default rotation overlap: long enough to redeploy, short enough to matter. */\nexport const DEFAULT_ROTATION_GRACE_MS = 3_600_000; // 1h\n\nexport function mintSecret(): string {\n return SECRET_PREFIX + randomBytes(32).toString('base64url');\n}\n\nexport function hashSecret(secret: string): string {\n return createHash('sha256').update(secret).digest('hex');\n}\n\n/** Digest comparison that does not leak a prefix match through timing. */\nfunction digestsEqual(a: string, b: string): boolean {\n const left = Buffer.from(a, 'hex');\n const right = Buffer.from(b, 'hex');\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\n/**\n * Constant-time comparison of a presented secret against an expected one.\n *\n * Both sides are hashed first, so the comparison is over two fixed-length\n * digests: `timingSafeEqual` throws on a length mismatch, which would\n * otherwise turn \"wrong length\" into a distinguishable error and leak the\n * secret's size. This is what a service holding ONE static shared secret\n * needs — a key store is the {@link ApiKeyAuth} path above. It lives here so\n * the signer's admin token and the engine's keys agree on what a constant-time\n * secret check is; the signer had its own copy until S52.\n */\nexport function secretsEqual(presented: string, expected: string): boolean {\n return digestsEqual(hashSecret(presented), hashSecret(expected));\n}\n\n/**\n * Persistence seam for keys, mirroring the engine's other ports. Auth is\n * authority state, so writes are persist-then-cache: `put` is awaited before a\n * key counts as issued, rotated, or revoked.\n */\nexport interface ApiKeyStorePort {\n put(record: ApiKeyRecord): MaybePromise<void>;\n get(id: string): ApiKeyRecord | undefined;\n /** Lookup by sha256 of a presented secret (current OR in-grace previous). */\n byHash(hash: string): ApiKeyRecord | undefined;\n list(): ApiKeyRecord[];\n}\n\nexport class InMemoryApiKeyStore implements ApiKeyStorePort {\n private readonly byId = new Map<string, ApiKeyRecord>();\n private readonly hashes = new Map<string, string>(); // secretHash -> keyId\n\n put(record: ApiKeyRecord): void {\n const existing = this.byId.get(record.id);\n if (existing) {\n this.hashes.delete(existing.secretHash);\n if (existing.previousSecretHash) this.hashes.delete(existing.previousSecretHash);\n }\n this.byId.set(record.id, record);\n this.hashes.set(record.secretHash, record.id);\n if (record.previousSecretHash) this.hashes.set(record.previousSecretHash, record.id);\n }\n\n get(id: string): ApiKeyRecord | undefined {\n return this.byId.get(id);\n }\n\n byHash(hash: string): ApiKeyRecord | undefined {\n const id = this.hashes.get(hash);\n return id === undefined ? undefined : this.byId.get(id);\n }\n\n list(): ApiKeyRecord[] {\n return [...this.byId.values()];\n }\n}\n\nexport type AuthFailureCode =\n | 'missing_credentials'\n | 'invalid_key'\n | 'key_revoked'\n /** Past the key's `expiresAt` (a sandbox key nobody claimed). */\n | 'key_expired'\n | 'secret_expired'\n | 'insufficient_scope'\n | 'unknown_key_id'\n /** The key is confined to an org, and the named agent is not in it. */\n | 'agent_not_in_scope'\n /**\n * The key is confined to an org and the route has no tenant rule, so there\n * is no way to confine the call. Fail closed: an unclassified route is\n * reachable only by an unscoped operator key, exactly as an unclassified\n * route already demands `admin`.\n */\n | 'route_not_scopable';\n\n/**\n * Every rejected request throws one of these. It carries the HTTP status so a\n * route never has to guess: 401 means \"we do not know who you are\", 403 means\n * \"we know, and this key may not do that\". Nothing fails open, nothing fails\n * silently.\n */\nexport class AuthError extends Error {\n /**\n * A brand, because `instanceof` is not enough here. Every service bundles\n * `@reinconsole/core` into its own output (`noExternal`, for self-contained\n * deploys), so an `ApiKeyAuth` built from one package and handed to a server\n * in another throws an AuthError from a DIFFERENT copy of this class. An\n * `instanceof` check in the catching service then misses it and a 401 turns\n * into a 500 — which is what happened the first time the graph was handed\n * the engine's auth object.\n */\n readonly reinAuthError = true;\n\n constructor(\n readonly status: 401 | 403 | 404,\n readonly code: AuthFailureCode,\n message: string,\n ) {\n super(message);\n this.name = 'AuthError';\n }\n\n /** Use this, never a bare `instanceof`, when catching across packages. */\n static is(err: unknown): err is AuthError {\n return (\n err instanceof AuthError ||\n (typeof err === 'object' &&\n err !== null &&\n (err as { reinAuthError?: unknown }).reinAuthError === true)\n );\n }\n}\n\nexport interface IssuedApiKey {\n key: ApiKey;\n /** The plaintext secret. Shown once, at issuance; never recoverable after. */\n secret: string;\n}\n\nexport interface ApiKeyAuthOptions {\n store?: ApiKeyStorePort;\n /** Injected clock, so grace-window behaviour is testable without waiting. */\n now?: () => number;\n}\n\nexport class ApiKeyAuth {\n private readonly store: ApiKeyStorePort;\n private readonly now: () => number;\n\n constructor(options: ApiKeyAuthOptions = {}) {\n this.store = options.store ?? new InMemoryApiKeyStore();\n this.now = options.now ?? Date.now;\n }\n\n /**\n * Mint a key. The secret in the result is the only copy that will exist.\n *\n * `orgId` confines the key to one tenant; omitting it mints the unscoped\n * operator key that every deployment had before tenancy. `agentIds` narrows\n * an org-scoped key to named agents and is refused without an org, because a\n * scope with no org cannot say which policies or approvers the key may\n * reach — and a half-defined scope is the kind that fails open.\n */\n async issue(input: {\n name: string;\n scopes: ApiKeyScope[];\n /** Confine the key to one org. Omit for an unscoped operator key. */\n orgId?: string;\n /** Narrow an org-scoped key to named agents (max 64). */\n agentIds?: string[];\n /** Adopt a caller-supplied secret (env-seeded boot keys). */\n secret?: string;\n /** Stop authenticating at this instant (sandbox keys). Omit for never. */\n expiresAt?: Date;\n }): Promise<IssuedApiKey> {\n if (input.scopes.length === 0) throw new TypeError('an API key needs at least one scope');\n if (input.agentIds?.length && input.orgId === undefined) {\n throw new TypeError('agentIds narrows an org-scoped key; pass orgId as well');\n }\n const secret = input.secret ?? mintSecret();\n const record = ApiKeyRecord.parse({\n id: newId('key'),\n name: input.name,\n scopes: input.scopes,\n ...(input.orgId !== undefined ? { orgId: input.orgId } : {}),\n ...(input.agentIds?.length ? { agentIds: input.agentIds } : {}),\n createdAt: new Date(this.now()),\n ...(input.expiresAt !== undefined ? { expiresAt: input.expiresAt } : {}),\n secretHash: hashSecret(secret),\n });\n await this.store.put(record);\n return { key: toPublicApiKey(record), secret };\n }\n\n /**\n * Mint a replacement secret for an existing key. The outgoing secret keeps\n * working for `graceMs` so callers can be redeployed one at a time; pass 0\n * to cut it off immediately (what a compromised secret needs).\n */\n async rotate(keyId: string, options: { graceMs?: number } = {}): Promise<IssuedApiKey> {\n const record = this.store.get(keyId);\n if (!record) throw new AuthError(404, 'unknown_key_id', `no such key: ${keyId}`);\n const graceMs = options.graceMs ?? DEFAULT_ROTATION_GRACE_MS;\n const secret = mintSecret();\n const rotated: ApiKeyRecord = {\n ...record,\n secretHash: hashSecret(secret),\n rotatedAt: new Date(this.now()),\n };\n delete rotated.previousSecretHash;\n delete rotated.previousSecretExpiresAt;\n if (graceMs > 0) {\n rotated.previousSecretHash = record.secretHash;\n rotated.previousSecretExpiresAt = new Date(this.now() + graceMs);\n }\n await this.store.put(rotated);\n return { key: toPublicApiKey(rotated), secret };\n }\n\n async revoke(keyId: string): Promise<ApiKey | undefined> {\n const record = this.store.get(keyId);\n if (!record) return undefined;\n const revoked: ApiKeyRecord = { ...record, revokedAt: new Date(this.now()) };\n await this.store.put(revoked);\n return toPublicApiKey(revoked);\n }\n\n list(): ApiKey[] {\n return this.store.list().map(toPublicApiKey);\n }\n\n /** One key's public record, for an ownership check before rotate/revoke. */\n get(keyId: string): ApiKey | undefined {\n const record = this.store.get(keyId);\n return record === undefined ? undefined : toPublicApiKey(record);\n }\n\n /** True once at least one key exists — what \"auth is configured\" means. */\n hasKeys(): boolean {\n return this.store.list().length > 0;\n }\n\n /**\n * Resolve a request's credentials, or throw. `scope` is what the route\n * needs; an `admin` key satisfies every scope, any other key must hold the\n * exact one.\n */\n authenticate(headers: IncomingHeaders, scope: ApiKeyScope): ApiKey {\n const presented = readCredential(headers);\n if (presented === undefined) {\n throw new AuthError(401, 'missing_credentials', 'missing API key (Authorization: Bearer ...)');\n }\n\n const hash = hashSecret(presented);\n const record = this.store.byHash(hash);\n if (!record) throw new AuthError(401, 'invalid_key', 'unknown API key');\n\n if (!digestsEqual(record.secretHash, hash)) {\n // Only the in-grace previous secret can reach here.\n const expiry = record.previousSecretExpiresAt;\n if (\n record.previousSecretHash === undefined ||\n !digestsEqual(record.previousSecretHash, hash) ||\n expiry === undefined ||\n this.now() >= expiry.getTime()\n ) {\n throw new AuthError(401, 'secret_expired', 'rotated API key secret is no longer accepted');\n }\n }\n\n if (record.revokedAt) throw new AuthError(401, 'key_revoked', 'API key has been revoked');\n if (record.expiresAt && this.now() >= record.expiresAt.getTime()) {\n throw new AuthError(401, 'key_expired', 'API key has expired');\n }\n\n if (!record.scopes.includes('admin') && !record.scopes.includes(scope)) {\n throw new AuthError(403, 'insufficient_scope', `API key lacks the \"${scope}\" scope`);\n }\n\n void this.touch(record);\n return toPublicApiKey(record);\n }\n\n /** Throttled write-through of `lastUsedAt` — never on the critical path. */\n private async touch(record: ApiKeyRecord): Promise<void> {\n const at = this.now();\n if (record.lastUsedAt && at - record.lastUsedAt.getTime() < TOUCH_INTERVAL_MS) return;\n try {\n await this.store.put({ ...record, lastUsedAt: new Date(at) });\n } catch {\n // Usage telemetry must never turn a valid request into a failed one.\n }\n }\n}\n\n/** Just enough of a request's headers to read a credential from. */\nexport interface IncomingHeaders {\n authorization?: string | string[] | undefined;\n 'x-api-key'?: string | string[] | undefined;\n}\n\nfunction first(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * `Authorization: Bearer <secret>` is the primary form; `X-Api-Key: <secret>`\n * is accepted too, because a fair share of agent runtimes only send that.\n */\nexport function readCredential(headers: IncomingHeaders): string | undefined {\n const auth = first(headers.authorization)?.trim();\n if (auth) {\n const match = /^Bearer\\s+(.+)$/i.exec(auth);\n if (match?.[1]) return match[1].trim();\n }\n const apiKey = first(headers['x-api-key'])?.trim();\n return apiKey === undefined || apiKey === '' ? undefined : apiKey;\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","randomBytes","z"]}
1
+ {"version":3,"sources":["../../../packages/core/src/ids.ts","../../../packages/core/src/api-key.ts","../../../packages/core/src/ulid.ts","../../../packages/core/src/chain.ts","../../../packages/core/src/money.ts","../../../packages/core/src/glob.ts","../../../packages/core/src/agent.ts","../../../packages/core/src/erc8004.ts","../../../packages/core/src/intent.ts","../../../packages/core/src/decision.ts","../../../packages/core/src/canonical.ts","../../../packages/core/src/session.ts","../../../packages/core/src/payment.ts","../../../packages/core/src/receipt.ts","../../../packages/core/src/gate-receipt.ts","../../../packages/core/src/policy.ts","../../../packages/core/src/liveness.ts","../../../packages/core/src/approval.ts","../../../packages/core/src/reputation.ts","../../../packages/core/src/events.ts","../src/evidence.ts","../src/intents.ts","../src/scoring.ts","../src/graph.ts","../src/server.ts","../../../packages/core/src/auth.ts"],"sourcesContent":["import { z } from 'zod';\n\n/** Crockford base32 ULID body (26 chars), as produced by `ulid()`. */\nconst ULID_BODY = '[0-9A-HJKMNP-TV-Z]{26}';\n\n/**\n * A zod schema for a Stripe-style prefixed ULID, e.g. `agt_01J7...`.\n * Centralizing this keeps id formats consistent across DB rows, API payloads,\n * and SDK types.\n */\nexport function prefixedId(prefix: string) {\n return z\n .string()\n .regex(new RegExp(`^${prefix}_${ULID_BODY}$`), `expected a \"${prefix}_\" prefixed id`);\n}\n\nexport const OrgId = prefixedId('org');\nexport const AgentId = prefixedId('agt');\nexport const PolicyId = prefixedId('pol');\nexport const IntentId = prefixedId('int');\nexport const DecisionId = prefixedId('dec');\nexport const ReceiptId = prefixedId('rcp');\nexport const SessionId = prefixedId('ses');\nexport const GateReceiptId = prefixedId('grc');\nexport const ApiKeyId = prefixedId('key');\nexport const ApproverKeyId = prefixedId('apk');\n\nexport type OrgId = z.infer<typeof OrgId>;\nexport type AgentId = z.infer<typeof AgentId>;\nexport type PolicyId = z.infer<typeof PolicyId>;\nexport type IntentId = z.infer<typeof IntentId>;\nexport type DecisionId = z.infer<typeof DecisionId>;\nexport type ReceiptId = z.infer<typeof ReceiptId>;\nexport type SessionId = z.infer<typeof SessionId>;\nexport type GateReceiptId = z.infer<typeof GateReceiptId>;\nexport type ApiKeyId = z.infer<typeof ApiKeyId>;\nexport type ApproverKeyId = z.infer<typeof ApproverKeyId>;\n","import { z } from 'zod';\nimport { AgentId, ApiKeyId, OrgId } from './ids.js';\n\n/**\n * What a key is allowed to reach. `admin` satisfies every requirement (it is\n * the issuing/rotating key); the rest are least-privilege grants so an agent\n * runtime carrying an `evaluate` key cannot rewrite policy, and an approval\n * relay cannot read the spend history.\n *\n * `report` is the graph's ingestion grant, and it is deliberately NOT folded\n * into `evaluate`: evidence written to the reputation graph is about OTHER\n * subjects, so a fleet key that may spend its own budget has no business\n * moving a vendor's score. The engine's own reporting routes ride `evaluate`\n * because there the reporter IS the spender — that reasoning does not reach\n * across to a shared graph.\n *\n * `identity` is the hosted console's grant (Sprint 13): it redeems claim codes\n * and turns a signed-in owner into a short-lived read key for their org. It is\n * useful only on an UNSCOPED key -- those routes have no tenant rule, so an\n * org-scoped key holding it is refused before the route runs -- and it reads\n * and spends nothing by itself.\n */\nexport const ApiKeyScope = z.enum(['read', 'evaluate', 'approve', 'report', 'identity', 'admin']);\nexport type ApiKeyScope = z.infer<typeof ApiKeyScope>;\n\n/**\n * The public view of an API key — everything except the secret. This is what\n * the API returns on list/issue; the secret itself is shown exactly once, at\n * issuance, and never persisted in recoverable form (see {@link ApiKeyRecord}).\n */\nexport const ApiKey = z.object({\n id: ApiKeyId,\n name: z.string().min(1).max(200),\n scopes: z.array(ApiKeyScope).min(1),\n /**\n * The org this key acts inside. ABSENT means unscoped — an operator key that\n * reads and writes across every org, which is what a self-hosted single-org\n * deployment and every env-seeded boot secret have always been. That is the\n * compatibility rule the whole tenancy design rests on: adding tenancy\n * changed no existing key's behaviour, because no existing key has this\n * field.\n *\n * Present, it is a hard confinement, not a default: the key cannot read,\n * spend, approve or govern outside the org, and a write it submits is\n * stamped with this org rather than the one the body asked for.\n */\n orgId: OrgId.optional(),\n /**\n * Narrow an org-scoped key further, to specific agents — what an agent\n * runtime should carry, so a stolen key spends one agent's budget rather\n * than the org's. Only meaningful alongside {@link ApiKey.orgId}: agent\n * narrowing without an org is refused at issuance, because the scope it\n * would describe has no org to resolve policies and approvers in.\n *\n * Capped at 64: a list long enough to need more is a fleet, and a fleet is\n * what the org scope is for.\n */\n agentIds: z.array(AgentId).max(64).optional(),\n createdAt: z.coerce.date(),\n /** Last successful authentication, for spotting keys that can be retired. */\n lastUsedAt: z.coerce.date().optional(),\n /** Set by revoke(); a revoked key never authenticates again. */\n revokedAt: z.coerce.date().optional(),\n /** Set by rotate(); with `previousSecretExpiresAt` it dates the overlap. */\n rotatedAt: z.coerce.date().optional(),\n /**\n * Past this instant the key stops authenticating, like a revoked one.\n * ABSENT means it never expires -- every key before the sandbox, and every\n * key an operator mints. Only the anonymous sandbox sets it, and a key minted\n * BY an expiring key inherits the deadline: a sandbox that could mint itself\n * a permanent key would not be a 7-day sandbox.\n */\n expiresAt: z.coerce.date().optional(),\n});\nexport type ApiKey = z.infer<typeof ApiKey>;\n\n/**\n * SERVER-SIDE ONLY. The stored form: the public record plus sha256 digests of\n * the secrets. Digests, not secrets — a stolen database yields nothing that\n * authenticates. Rotation keeps the outgoing digest alive until\n * `previousSecretExpiresAt` so a running fleet can roll over without a\n * flag-day restart; past that instant only the current secret verifies.\n *\n * This shape never leaves the engine: every route answers with {@link ApiKey}.\n */\nexport const ApiKeyRecord = ApiKey.extend({\n secretHash: z.string(),\n previousSecretHash: z.string().optional(),\n previousSecretExpiresAt: z.coerce.date().optional(),\n});\nexport type ApiKeyRecord = z.infer<typeof ApiKeyRecord>;\n\n/** Strip the digests: the only sanctioned way to put a key on the wire. */\nexport function toPublicApiKey(record: ApiKeyRecord): ApiKey {\n const { secretHash, previousSecretHash, previousSecretExpiresAt, ...pub } = record;\n void secretHash;\n void previousSecretHash;\n void previousSecretExpiresAt;\n return pub;\n}\n","/**\n * Minimal ULID generation, dependency-free. ULIDs are lexicographically\n * sortable (time-prefixed) and url-safe, which is why Rein uses them for all\n * primary keys instead of UUIDs. Works in Node 22 and modern browsers/edge via\n * the WebCrypto `globalThis.crypto`.\n */\n\n// Crockford's base32 alphabet (excludes I, L, O, U to avoid ambiguity).\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\n\nfunction randomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n\nfunction encodeTime(now: number): string {\n let time = now;\n let out = '';\n for (let i = TIME_LEN - 1; i >= 0; i--) {\n out = ENCODING.charAt(time % 32) + out;\n time = Math.floor(time / 32);\n }\n return out;\n}\n\nfunction encodeRandom(): string {\n let out = '';\n for (const byte of randomBytes(RANDOM_LEN)) {\n out += ENCODING.charAt(byte % 32);\n }\n return out;\n}\n\n/** Generate a 26-character ULID. */\nexport function ulid(seedTime: number = Date.now()): string {\n return encodeTime(seedTime) + encodeRandom();\n}\n\n/** Generate a Stripe-style prefixed id, e.g. `newId('agt')` -> `agt_01J...`. */\nexport function newId<P extends string>(prefix: P): `${P}_${string}` {\n return `${prefix}_${ulid()}`;\n}\n","import { z } from 'zod';\n\n/**\n * Chains Rein observes/governs. x402 is multi-chain; Base and Solana ship\n * first, Polygon and BNB follow. The policy/ledger domain is rail-agnostic —\n * this enum is the only place chains are enumerated.\n */\nexport const Chain = z.enum(['base', 'solana', 'polygon', 'bnb']);\nexport type Chain = z.infer<typeof Chain>;\n\n/** Stablecoins settled over x402. */\nexport const Asset = z.enum(['USDC', 'USDT', 'EURC']);\nexport type Asset = z.infer<typeof Asset>;\n","import { z } from 'zod';\n\n/**\n * Monetary amounts in Rein are always non-negative **decimal strings**, never\n * floats. Float arithmetic silently loses precision on values like 0.1 + 0.2,\n * which is unacceptable for money. All comparison/aggregation goes through the\n * BigInt-backed helpers below.\n */\nexport const DecimalString = z\n .string()\n .regex(/^\\d+(\\.\\d+)?$/, 'must be a non-negative decimal string, e.g. \"5.00\"');\nexport type DecimalString = z.infer<typeof DecimalString>;\n\nconst DECIMAL_RE = /^\\d+(\\.\\d+)?$/;\n\nexport function isValidDecimal(value: string): boolean {\n return DECIMAL_RE.test(value);\n}\n\nfunction assertDecimal(value: string): void {\n if (!isValidDecimal(value)) {\n throw new TypeError(`invalid decimal string: ${JSON.stringify(value)}`);\n }\n}\n\nfunction fractionLength(value: string): number {\n const dot = value.indexOf('.');\n return dot === -1 ? 0 : value.length - dot - 1;\n}\n\n/** Scale a decimal string into an integer BigInt at a fixed number of fraction digits. */\nfunction toScaled(value: string, scale: number): bigint {\n const dot = value.indexOf('.');\n const intPart = dot === -1 ? value : value.slice(0, dot);\n const fracPart = dot === -1 ? '' : value.slice(dot + 1);\n const paddedFrac = (fracPart + '0'.repeat(scale)).slice(0, scale);\n return BigInt(intPart + paddedFrac);\n}\n\n/** Render a scaled BigInt back to a normalized decimal string (no trailing zeros). */\nfunction fromScaled(scaled: bigint, scale: number): string {\n if (scale === 0) return scaled.toString();\n const digits = scaled.toString().padStart(scale + 1, '0');\n const intPart = digits.slice(0, digits.length - scale);\n const fracPart = digits.slice(digits.length - scale).replace(/0+$/, '');\n return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;\n}\n\n/** Compare two decimal strings. Returns -1 (a<b), 0 (a==b), or 1 (a>b). */\nexport function compareDecimal(a: string, b: string): -1 | 0 | 1 {\n assertDecimal(a);\n assertDecimal(b);\n const scale = Math.max(fractionLength(a), fractionLength(b));\n const av = toScaled(a, scale);\n const bv = toScaled(b, scale);\n return av < bv ? -1 : av > bv ? 1 : 0;\n}\n\n/** Sum a list of decimal strings without floating-point error. */\nexport function sumDecimal(values: readonly string[]): string {\n if (values.length === 0) return '0';\n let scale = 0;\n for (const v of values) {\n assertDecimal(v);\n scale = Math.max(scale, fractionLength(v));\n }\n let total = 0n;\n for (const v of values) total += toScaled(v, scale);\n return fromScaled(total, scale);\n}\n\n/**\n * `a - b` for decimal strings, exact. Requires `a >= b`: the one caller is\n * \"how much more settled than was allowed\", and a negative money string is a\n * shape nothing else in the system accepts, so asking for one is a bug here\n * rather than a value to hand on.\n */\nexport function subDecimal(a: string, b: string): string {\n if (compareDecimal(a, b) < 0) {\n throw new RangeError(`subDecimal: ${a} is less than ${b}`);\n }\n const scale = Math.max(fractionLength(a), fractionLength(b));\n return fromScaled(toScaled(a, scale) - toScaled(b, scale), scale);\n}\n\n/** Multiply two decimal strings exactly (e.g. median * \"3\" for price-sanity). */\nexport function mulDecimal(a: string, b: string): string {\n assertDecimal(a);\n assertDecimal(b);\n const scaleA = fractionLength(a);\n const scaleB = fractionLength(b);\n const product = toScaled(a, scaleA) * toScaled(b, scaleB);\n return fromScaled(product, scaleA + scaleB);\n}\n\n/** Convenience: a > b. */\nexport function gt(a: string, b: string): boolean {\n return compareDecimal(a, b) === 1;\n}\n\n/** Convenience: a < b. */\nexport function lt(a: string, b: string): boolean {\n return compareDecimal(a, b) === -1;\n}\n","/**\n * Minimal, safe glob matcher supporting only the `*` wildcard (matches any run\n * of characters, including none). Used for vendor host allowlists\n * (e.g. \"*.trusted.io\"), agent-id targeting (e.g. \"agt_research_*\"), and\n * Gate route pricing (e.g. \"/api/reports/*\").\n *\n * Deliberately NOT a regex from user input — the pattern is escaped so a\n * malicious policy value cannot inject regex behavior (ReDoS, etc.).\n */\nexport function globMatch(pattern: string, value: string): boolean {\n const escaped = pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // escape regex metachars\n const withWildcard = escaped.replace(/\\\\\\*/g, '.*'); // re-enable only `*`\n return new RegExp(`^${withWildcard}$`).test(value);\n}\n\n/** True if `value` matches any pattern in the list. */\nexport function globMatchAny(patterns: readonly string[], value: string): boolean {\n return patterns.some((p) => globMatch(p, value));\n}\n","import { z } from 'zod';\nimport { AgentId, OrgId } from './ids.js';\nimport { Chain } from './chain.js';\n\n/**\n * Enforcement tier of a given agent wallet:\n * - `observed` — Rein sees spend on-chain but has no control (no SDK either).\n * - `sdk` — agent uses @reinconsole/sdk; advisory + observability (bypassable).\n * - `session-key` — signer-level scope; out-of-policy txs cannot be signed.\n */\nexport const EnforcementMode = z.enum(['observed', 'sdk', 'session-key']);\nexport type EnforcementMode = z.infer<typeof EnforcementMode>;\n\nexport const AgentWallet = z.object({\n chain: Chain,\n address: z.string().min(1),\n mode: EnforcementMode,\n});\nexport type AgentWallet = z.infer<typeof AgentWallet>;\n\nexport const AgentStatus = z.enum(['active', 'frozen']);\nexport type AgentStatus = z.infer<typeof AgentStatus>;\n\n/**\n * A semantic grouping label, e.g. \"research\" or \"prod-trading\". Lowercase\n * slugs only — policy `appliesTo.labels` matches these (with globs), and a\n * case-normalization mismatch would silently un-target a policy.\n */\nexport const AgentLabel = z\n .string()\n .max(40)\n .regex(/^[a-z0-9][a-z0-9-]*$/, 'label must be a lowercase slug like \"research\" or \"prod-trading\"');\nexport type AgentLabel = z.infer<typeof AgentLabel>;\n\nexport const Agent = z.object({\n id: AgentId,\n orgId: OrgId,\n name: z.string().min(1).max(200),\n /** On-chain ERC-8004 identity, if the agent is registered. */\n erc8004Id: z.string().optional(),\n /** Semantic grouping labels — the targets of policy `appliesTo.labels`. */\n labels: z\n .array(AgentLabel)\n .max(16)\n .refine((ls) => new Set(ls).size === ls.length, { message: 'labels must be unique' })\n .default([]),\n wallets: z.array(AgentWallet).default([]),\n status: AgentStatus.default('active'),\n createdAt: z.coerce.date(),\n});\nexport type Agent = z.infer<typeof Agent>;\n","import { z } from 'zod';\n\n/**\n * The canonical string form of an ERC-8004 agent identity:\n *\n * eip155:{chainId}:{identityRegistry}/{tokenId}\n *\n * The part before the `/` is the spec's registry reference verbatim (the\n * `agentRegistry` field of a registration file, CAIP-10 account form); the\n * tokenId is the ERC-721 id the Identity Registry minted (`agentId` in the\n * spec). This exact string is what `Agent.erc8004Id` / `Vendor.erc8004Id`\n * carry, AND the reputation-subject id a registered agent's evidence keys by.\n *\n * Casing is load-bearing: reputation subject keys for agent-kind ids are\n * case-sensitive unless they start with `0x` (see @reinconsole/graph\n * `normalizeSubject`), and an eip155 string does not. `formatErc8004Id`\n * therefore always emits the registry address in lowercase, and consumers must\n * never hand-build the string — parse then re-format to normalize.\n */\n\nexport interface Erc8004Ref {\n /** EIP-155 chain id (e.g. 8453 Base, 84532 Base Sepolia). */\n chainId: number;\n /** Identity Registry contract address, lowercase 0x-hex. */\n registry: string;\n /** ERC-721 tokenId — the spec's `agentId`. bigint: token ids exceed 2^53. */\n tokenId: bigint;\n}\n\nconst REGISTRY_PATTERN = /^0x[0-9a-fA-F]{40}$/;\nconst ID_PATTERN = /^eip155:(\\d+):(0x[0-9a-fA-F]{40})\\/(\\d+)$/;\n\n/** Build the canonical id string. Throws on a malformed ref (programmer error). */\nexport function formatErc8004Id(ref: Erc8004Ref): string {\n if (!Number.isSafeInteger(ref.chainId) || ref.chainId < 1) {\n throw new RangeError(`erc8004: bad chainId ${ref.chainId}`);\n }\n if (!REGISTRY_PATTERN.test(ref.registry)) {\n throw new RangeError(`erc8004: bad registry address ${ref.registry}`);\n }\n if (typeof ref.tokenId !== 'bigint' || ref.tokenId < 0n) {\n // The typeof guard matters for plain-JS callers: 1.5 < 0n is legal JS and\n // false, and would mint the unparseable \"…/1.5\".\n throw new RangeError(`erc8004: bad tokenId ${ref.tokenId}`);\n }\n return `eip155:${ref.chainId}:${ref.registry.toLowerCase()}/${ref.tokenId}`;\n}\n\n/**\n * Parse a canonical (or hand-written) id string. Accepts any address casing\n * and returns a lowercase ref, or undefined when malformed — so\n * `formatErc8004Id(parseErc8004Id(x)!)` is the normalization step.\n */\nexport function parseErc8004Id(value: string): Erc8004Ref | undefined {\n const match = ID_PATTERN.exec(value);\n if (!match) return undefined;\n const chainId = Number(match[1]);\n if (!Number.isSafeInteger(chainId) || chainId < 1) return undefined;\n return {\n chainId,\n registry: match[2]!.toLowerCase(),\n tokenId: BigInt(match[3]!),\n };\n}\n\n/** Strict boundary schema for the id string (the fields themselves stay free-form). */\nexport const Erc8004Id = z\n .string()\n .refine((value) => parseErc8004Id(value) !== undefined, {\n message: 'expected \"eip155:{chainId}:{registry}/{tokenId}\"',\n });\nexport type Erc8004Id = z.infer<typeof Erc8004Id>;\n","import { z } from 'zod';\nimport { AgentId, IntentId } from './ids.js';\nimport { Chain, Asset } from './chain.js';\nimport { DecimalString } from './money.js';\n\nexport const Vendor = z.object({\n host: z.string().min(1),\n address: z.string().min(1),\n erc8004Id: z.string().optional(),\n});\nexport type Vendor = z.infer<typeof Vendor>;\n\n/**\n * The observability gold: links every micro-payment back to the task and run\n * that caused it, so finance teams can answer \"what was this spend for?\".\n */\nexport const TaskContext = z.object({\n taskId: z.string().optional(),\n parentRunId: z.string().optional(),\n purpose: z.string().max(500).optional(),\n});\nexport type TaskContext = z.infer<typeof TaskContext>;\n\n/**\n * A request to spend, submitted to the policy engine BEFORE any signature is\n * released. The `nonce` provides replay protection on the signer path.\n */\nexport const PaymentIntent = z.object({\n id: IntentId,\n agentId: AgentId,\n vendor: Vendor,\n resource: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n taskContext: TaskContext.default({}),\n nonce: z.string().min(1),\n createdAt: z.coerce.date(),\n});\nexport type PaymentIntent = z.infer<typeof PaymentIntent>;\n\n/**\n * Reduce an intent's `resource` to its path for policy matching. The value is\n * vendor-declared and shape-varies: the x402 spec puts a full URL in\n * `requirement.resource`, while the guard falls back to the request pathname\n * when the vendor omits it. Policies match on the PATH so authors never need\n * to know which shape a vendor emits; host targeting stays with\n * `vendorHostIn`, which is derived from the real request URL rather than the\n * vendor's own claim. Query strings do not survive the reduction.\n */\nexport function resourcePathOf(resource: string): string {\n try {\n const url = new URL(resource);\n if (url.protocol === 'http:' || url.protocol === 'https:') return url.pathname;\n } catch {\n // Not a URL — already a bare path.\n }\n return resource;\n}\n","import { z } from 'zod';\nimport { AgentId, DecisionId, IntentId } from './ids.js';\n\nexport const DecisionOutcome = z.enum(['allow', 'deny', 'escalate']);\nexport type DecisionOutcome = z.infer<typeof DecisionOutcome>;\n\n/**\n * The signed, hash-chained record of a single policy evaluation. Written BEFORE\n * any signature is released. `prevHash` + `hash` form a tamper-evident chain;\n * `signature` is the policy service's signing key over `hash`. The eventual\n * on-chain `txHash` is attached later by the indexer (see SettledPayment).\n */\nexport const Decision = z.object({\n id: DecisionId,\n intentId: IntentId,\n /**\n * sha256 of the intent's canonical content (see canonical.ts). Binds the\n * decision to the exact transfer it judged — amount, recipient, asset,\n * chain — so a signer can verify an {intent, decision} pair offline as a\n * self-contained spend voucher, not just a reference by id.\n */\n intentHash: z.string(),\n outcome: DecisionOutcome,\n /** Ids of the rules that fired, for explainability. */\n matchedRules: z.array(z.string()).default([]),\n /** Human-readable explanation surfaced in the dashboard. */\n reason: z.string().optional(),\n policyId: z.string(),\n policyVersion: z.string(),\n /** Hash of the previous decision in the chain (tamper-evident log). */\n prevHash: z.string(),\n /** Hash of this decision's canonical content. */\n hash: z.string(),\n /** Service signing-key signature over `hash`. */\n signature: z.string(),\n latencyMs: z.number().nonnegative(),\n decidedAt: z.coerce.date(),\n});\nexport type Decision = z.infer<typeof Decision>;\n\n/**\n * A decision as `GET /v1/decisions` serves it (0.3.0): the signed record plus\n * `agentId`, the agent whose intent it judged.\n *\n * `agentId` is an ENVELOPE field -- the engine's own attribution, outside\n * `hash` and `signature`, so a verified chain vouches for nothing about it.\n * Putting it inside would have changed the hash of every future decision and\n * split the chain into two formats. It is here for a reconciler that is not\n * co-located with the agent: `reconcile()` needs an allowed intent's agent,\n * or agent B's payment against A's intent is credited to A (S74), and before\n * this field only `/v1/reconciliation` carried it -- and erases it the moment\n * a settlement lands. Absent on an unattributed (pre-tenancy) row.\n */\nexport const AttributedDecision = Decision.extend({ agentId: AgentId.optional() });\nexport type AttributedDecision = z.infer<typeof AttributedDecision>;\n","import type { PaymentIntent } from './intent.js';\nimport type { DecisionOutcome } from './decision.js';\nimport type { ApprovalVerdict } from './approval.js';\n\n/**\n * Canonical byte forms shared by the policy engine (which hashes and signs\n * them) and the signer (which verifies them offline). Field order is fixed,\n * dates are ISO strings, and absent optionals are explicit nulls, so the same\n * parsed object always canonicalizes to the same bytes — including after a\n * JSON round-trip over HTTP.\n *\n * These builders are pure (no node:crypto) so @reinconsole/core stays loadable in a\n * browser; services hash the strings with whatever sha256 they have.\n */\n\n/** Canonical form of an intent — what `Decision.intentHash` commits to. */\nexport function canonicalIntent(intent: PaymentIntent): string {\n return JSON.stringify({\n id: intent.id,\n agentId: intent.agentId,\n vendor: {\n host: intent.vendor.host,\n address: intent.vendor.address,\n erc8004Id: intent.vendor.erc8004Id ?? null,\n },\n resource: intent.resource,\n amount: intent.amount,\n asset: intent.asset,\n chain: intent.chain,\n taskContext: {\n taskId: intent.taskContext.taskId ?? null,\n parentRunId: intent.taskContext.parentRunId ?? null,\n purpose: intent.taskContext.purpose ?? null,\n },\n nonce: intent.nonce,\n createdAt: intent.createdAt.toISOString(),\n });\n}\n\n/** The decision fields covered by `Decision.hash` (and thus its signature). */\nexport interface DecisionContent {\n intentId: string;\n intentHash: string;\n outcome: DecisionOutcome;\n matchedRules: string[];\n policyId: string;\n policyVersion: string;\n prevHash: string;\n decidedAt: Date;\n}\n\n/** Canonical form of a decision — what `Decision.hash` is computed over. */\nexport function canonicalDecision(d: DecisionContent): string {\n return JSON.stringify({\n intentId: d.intentId,\n intentHash: d.intentHash,\n outcome: d.outcome,\n matchedRules: d.matchedRules,\n policyId: d.policyId,\n policyVersion: d.policyVersion,\n prevHash: d.prevHash,\n decidedAt: d.decidedAt.toISOString(),\n });\n}\n\n/** The fields an approval signature commits to. */\nexport interface ApprovalContent {\n decisionId: string;\n intentHash: string;\n verdict: ApprovalVerdict;\n}\n\n/**\n * Canonical form of an approval — the exact bytes an approver signs.\n *\n * Three properties are deliberate. The leading `rein` domain tag separates\n * this signature space from the decision log's, so a decision signature can\n * never be replayed as an approval or the reverse. `decisionId` makes each\n * challenge single-use: one escalation, one decision id, one signature that\n * means anything. And `verdict` is INSIDE the signed bytes — without it, a\n * captured approval could be resubmitted as a rejection (or vice versa) by\n * anyone who saw it in flight.\n */\nexport function canonicalApproval(a: ApprovalContent): string {\n return JSON.stringify({\n rein: 'approval/v1',\n decisionId: a.decisionId,\n intentHash: a.intentHash,\n verdict: a.verdict,\n });\n}\n","import { z } from 'zod';\nimport { AgentId, SessionId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/**\n * A session-key grant: the signer tier's unit of delegated authority. The\n * agent process holds only the bearer token — the wallet key never leaves the\n * signer — and the stored record keeps a hash of the token, never the token\n * itself (it is returned exactly once, at creation).\n *\n * Caps here are the signer-side backstop UNDER whatever policy says: a\n * signature is released only when the engine allowed the intent AND the\n * session has room. Amounts are decimal strings in the asset's human units.\n */\nexport const Session = z.object({\n id: SessionId,\n agentId: AgentId,\n /** sha256 hex of the bearer token. */\n tokenHash: z.string(),\n /** Cumulative ceiling across the session's lifetime. Absent = uncapped. */\n capAmount: DecimalString.optional(),\n /** Ceiling per individual signature. Absent = uncapped. */\n maxPerPayment: DecimalString.optional(),\n expiresAt: z.coerce.date(),\n createdAt: z.coerce.date(),\n revokedAt: z.coerce.date().optional(),\n});\nexport type Session = z.infer<typeof Session>;\n","import { z } from 'zod';\nimport { IntentId } from './ids.js';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\n\n/**\n * On-chain confirmation of a settled payment, reconciled by the indexer against\n * the originating intent (amount + recipient + nonce, or facilitator webhook).\n */\nexport const SettledPayment = z.object({\n intentId: IntentId,\n txHash: z.string(),\n chain: Chain,\n blockNumber: z.coerce.bigint(),\n facilitator: z.string().optional(),\n feePaid: DecimalString.optional(),\n confirmedAt: z.coerce.date(),\n});\nexport type SettledPayment = z.infer<typeof SettledPayment>;\n\n/**\n * A report that an allowed intent's payment actually landed — the settlement\n * half of reconciliation (\"allowed but never settled\", B1).\n *\n * Deliberately looser than {@link SettledPayment}: the on-chain confirmation\n * requires a tx hash and a block, while a REPORT is whatever the observer\n * could see. A vendor's `X-PAYMENT-RESPONSE` names a transaction but no block;\n * a facilitator webhook may name neither. Demanding the strong shape would\n * mean the weakest observers — the ones a deployment actually has — could\n * report nothing at all, and every allowance would read as a gap.\n *\n * `source` records WHO said so, because the evidence is not all equal: an\n * independent indexer watching the chain is the strong claim, while a guard\n * reporting on its own payment is the agent vouching for itself. Nothing here\n * grants authority — a settlement report can only ever CLOSE a gap in the\n * reconciliation report, never authorize a payment or alter a decision.\n */\nexport const SettlementReport = z.object({\n intentId: IntentId,\n /** The settling transaction, when the observer saw one. */\n txHash: z.string().optional(),\n chain: Chain.optional(),\n /** The amount as SETTLED, when known — may differ from the amount allowed. */\n amount: DecimalString.optional(),\n /** Free-form observer tag, e.g. \"indexer\", \"guard\", \"facilitator\". */\n source: z.string().min(1).max(64).optional(),\n confirmedAt: z.coerce.date(),\n});\nexport type SettlementReport = z.infer<typeof SettlementReport>;\n","import { z } from 'zod';\nimport { ReceiptId, IntentId, DecisionId, AgentId } from './ids.js';\nimport { Chain, Asset } from './chain.js';\nimport { DecimalString } from './money.js';\nimport { TaskContext } from './intent.js';\nimport { DecisionOutcome } from './decision.js';\n\n/**\n * What the SDK reports back after settlement: the `X-PAYMENT-RESPONSE` header\n * a vendor returns once payment clears. Mock facilitators fill this in v0.1;\n * the indexer reconciles it against on-chain truth later (see SettledPayment).\n */\nexport const ReceiptSettlement = z.object({\n txHash: z.string().optional(),\n networkId: z.string().optional(),\n /** Raw header payload, kept for forensics until the indexer confirms. */\n raw: z.string().optional(),\n});\nexport type ReceiptSettlement = z.infer<typeof ReceiptSettlement>;\n\n/**\n * The SDK-side record of one guarded payment attempt: which request triggered\n * it, what the engine decided, and (if allowed and paid) how it settled.\n * Receipts are the observability primitive — every 402 the guard touches\n * produces exactly one, whether the payment was allowed or blocked.\n */\nexport const Receipt = z.object({\n id: ReceiptId,\n agentId: AgentId,\n intentId: IntentId,\n decisionId: DecisionId,\n outcome: DecisionOutcome,\n /** The URL the agent actually fetched (the paywalled resource). */\n url: z.string(),\n method: z.string().default('GET'),\n vendorHost: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n taskContext: TaskContext.default({}),\n /** Human-readable explanation copied from the decision. */\n reason: z.string().optional(),\n /** Present only once a payment was made and the vendor confirmed it. */\n settlement: ReceiptSettlement.optional(),\n createdAt: z.coerce.date(),\n});\nexport type Receipt = z.infer<typeof Receipt>;\n","import { z } from 'zod';\nimport { GateReceiptId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/**\n * The vendor-side twin of `Receipt`: one settled x402 payment as the GATE saw\n * it — who paid, for which priced route, and the settlement transaction. The\n * agent-side Receipt records what an agent tried to spend; a GateReceipt\n * records what a vendor actually earned.\n */\nexport const GateReceipt = z.object({\n id: GateReceiptId,\n at: z.coerce.date(),\n /** The route pattern that priced this request, e.g. \"/api/reports/*\". */\n route: z.string().min(1),\n /** The concrete resource paid for (URL path actually requested). */\n resource: z.string().min(1),\n method: z.string().min(1),\n /** The paying wallet address, as asserted by the settled payment. */\n payer: z.string().min(1),\n payTo: z.string().min(1),\n /** Human-unit decimal amount, e.g. \"0.05\". */\n amount: DecimalString,\n /** The same amount in the asset's atomic units, e.g. \"50000\". */\n amountAtomic: z.string().regex(/^\\d+$/, 'atomic amount must be an integer string'),\n /** Token symbol or contract address, exactly as quoted in the requirement. */\n asset: z.string().min(1),\n network: z.string().min(1),\n /** Settlement transaction hash (mock ledger or on-chain). */\n transaction: z.string().min(1),\n});\nexport type GateReceipt = z.infer<typeof GateReceipt>;\n","import { z } from 'zod';\nimport { Chain } from './chain.js';\nimport { OrgId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/** A rolling-window spec, e.g. \"24h\", \"1h\", \"30m\", \"7d\". */\nexport const Window = z\n .string()\n .regex(/^\\d+[smhd]$/, 'window must be like \"30s\", \"15m\", \"1h\", or \"7d\"');\nexport type Window = z.infer<typeof Window>;\n\n/** A relative multiplier, e.g. \"3x\" (used for price-sanity checks). */\nexport const Multiplier = z.string().regex(/^\\d+(\\.\\d+)?x$/, 'multiplier must be like \"3x\"');\nexport type Multiplier = z.infer<typeof Multiplier>;\n\n/**\n * The set of predicates a rule can test. Multiple predicates in one condition\n * are ANDed together. Each maps to an evaluator in the policy engine.\n */\nexport const Condition = z\n .object({\n /** Per-transaction amount exceeds this value. */\n amountGt: DecimalString.optional(),\n /** Sum of spend in a rolling window exceeds `gt`. */\n rollingSum: z.object({ window: Window, gt: DecimalString }).optional(),\n /** Transaction count in a rolling window exceeds `gt`. */\n txCount: z.object({ window: Window, gt: z.number().int().nonnegative() }).optional(),\n /** Vendor host matches one of these patterns (supports `*` globs). */\n vendorHostIn: z.array(z.string()).optional(),\n /**\n * Resource PATH matches one of these patterns (supports `*` globs, e.g.\n * \"/v1/reports/*\"). A full-URL resource is reduced to its pathname before\n * matching (see `resourcePathOf`), so the same pattern works whether the\n * vendor declares \"https://api.x.com/v1/answer\" or \"/v1/answer\". Pair\n * with `vendorHostIn` to scope a path rule to specific vendors.\n */\n resourceIn: z.array(z.string()).optional(),\n /** First time Rein has seen this vendor for the agent. */\n vendorFirstSeen: z.boolean().optional(),\n /** Vendor reputation score is below this threshold (Phase 3 hook). */\n vendorReputationLt: z.number().min(0).max(100).optional(),\n /** Amount is more than `gt`-times the observed median for this resource. */\n amountVsResourceMedian: z.object({ gt: Multiplier }).optional(),\n /**\n * Cumulative spend attributed to THIS intent's `taskContext.taskId`\n * (prior spend on the task plus this payment) exceeds `gt`. Scopes a\n * budget to one unit of work rather than to a window: a research run that\n * is meant to cost a dollar cannot quietly cost fifty, however slowly.\n *\n * An intent carrying no `taskId` cannot be attributed to a task, so the\n * predicate never triggers for one. Requiring attribution is a separate,\n * deliberate rule (deny on `taskIdMissing`), not a side effect of setting\n * a budget — otherwise every untagged probe payment would trip every\n * task budget in the policy.\n */\n taskBudget: z.object({ gt: DecimalString }).optional(),\n /** The intent carries no `taskContext.taskId` (or an empty one). */\n taskIdMissing: z.boolean().optional(),\n })\n .refine((c) => Object.values(c).some((v) => v !== undefined), {\n message: 'a condition must specify at least one predicate',\n });\nexport type Condition = z.infer<typeof Condition>;\n\n/**\n * A behavioral circuit breaker: one primitive with three tripwires — a window\n * (rate), a transaction count, and a value cap — measured over the agent's\n * own recent activity.\n *\n * A breaker is not a rule. Rules ask about the intent in front of them; a\n * breaker asks whether the agent's BEHAVIOR has left the envelope it was\n * given, and once it has, every subsequent intent ESCALATES for a signed\n * approval. It never denies on its own: an agent that trips a breaker in the\n * middle of a job must be able to be waved through by a human, because a\n * silent deny at the wrong moment strands the work with no path forward and\n * no one told (the failure mode Flash's exit paths taught).\n *\n * Reset happens two ways, and they are one mechanism: the window rolling\n * forward, or a signed approval, which moves the breaker's counting floor to\n * now. Nothing \"un-trips\" a breaker by decree — the activity simply stops\n * being inside the measured span.\n *\n * Targeting is the policy's job: `appliesTo.agents` / `appliesTo.labels`\n * decide WHICH agents carry the breaker. COUNTING is always per agent — a\n * breaker pooled across every agent sharing a label needs a cross-agent\n * aggregate the per-agent spend context cannot express, and is deferred.\n */\nexport const Breaker = z\n .object({\n id: z.string().min(1),\n /** The trailing span the tripwires measure over. */\n window: Window,\n /**\n * Trip when the window would hold MORE than this many transactions.\n * `10` permits ten in the window and escalates the eleventh.\n */\n txCount: z.number().int().positive().optional(),\n /**\n * Trip when the window's spend would exceed this total. `\"50.00\"` permits\n * a window totalling exactly 50.00 and escalates the payment that would\n * carry it past. Prospective, like every other spend predicate: the\n * payment that would breach the cap is the one that escalates, rather\n * than the innocent one after it.\n */\n valueCap: DecimalString.optional(),\n })\n .refine((b) => b.txCount !== undefined || b.valueCap !== undefined, {\n message: 'a breaker must specify at least one of txCount / valueCap',\n });\nexport type Breaker = z.infer<typeof Breaker>;\n\n/**\n * A single rule: an id plus exactly one action (allow / deny / escalate) whose\n * value is the condition that triggers it.\n */\nexport const Rule = z\n .object({\n id: z.string().min(1),\n allow: Condition.optional(),\n deny: Condition.optional(),\n escalate: Condition.optional(),\n })\n .refine((r) => [r.allow, r.deny, r.escalate].filter((v) => v !== undefined).length === 1, {\n message: 'a rule must specify exactly one of allow / deny / escalate',\n });\nexport type Rule = z.infer<typeof Rule>;\n\nexport const PolicyDefault = z.enum(['allow', 'deny']);\nexport type PolicyDefault = z.infer<typeof PolicyDefault>;\n\nexport const AppliesTo = z.object({\n /** Agent id patterns (supports `*` globs, e.g. \"agt_research_*\"). */\n agents: z.array(z.string()).optional(),\n /**\n * Agent label patterns (supports `*` globs) — semantic targeting. Matches\n * when the agent carries at least one label matching any pattern. Requires\n * the agent DOCUMENT: an intent from an unregistered agent never matches a\n * labels-targeted policy (and so fails closed when no other policy applies).\n */\n labels: z.array(z.string()).optional(),\n chains: z.array(Chain).optional(),\n});\nexport type AppliesTo = z.infer<typeof AppliesTo>;\n\nexport const Escalation = z.object({\n approvers: z.array(z.string()).default([]),\n timeoutAction: PolicyDefault.default('deny'),\n timeoutMin: z.number().int().positive().default(15),\n});\nexport type Escalation = z.infer<typeof Escalation>;\n\n/**\n * A declarative, versioned, immutable-once-active policy. Evaluation order in\n * the engine is: explicit DENY > ESCALATE > ALLOW > `default`.\n */\nexport const Policy = z.object({\n policyId: z.string().min(1),\n version: z.string().default('1'),\n /**\n * The org this policy governs. ABSENT means GLOBAL — it is considered for\n * every agent, which is what every policy written before tenancy existed is\n * and stays.\n *\n * This is separate from {@link AppliesTo} on purpose, and it has to be:\n * `appliesTo: {}` matches every agent, so a tenant's policy carrying the\n * default targeting would otherwise govern other tenants' agents the moment\n * it was written. Org scoping is applied BEFORE targeting — a policy whose\n * `orgId` is not the agent's org is not a candidate at all.\n */\n orgId: OrgId.optional(),\n appliesTo: AppliesTo.default({}),\n rules: z.array(Rule).default([]),\n /**\n * Behavioral breakers evaluated alongside the rules. A tripped breaker\n * escalates at the same precedence as an `escalate` rule — so an explicit\n * DENY still wins, and an ALLOW rule cannot wave past a tripped breaker.\n */\n breakers: z.array(Breaker).default([]),\n default: PolicyDefault.default('deny'),\n /** Below this amount, fail-open is permitted during a policy-service outage. */\n denyFloor: DecimalString.default('0.05'),\n escalation: Escalation.optional(),\n});\nexport type Policy = z.infer<typeof Policy>;\n","import { z } from 'zod';\nimport { AgentId } from './ids.js';\nimport { Window } from './policy.js';\n\n/**\n * Dead-man monitoring: the alarm for an agent that stopped (Phase B2).\n *\n * Every other control in Rein answers \"should this payment happen?\". This one\n * answers the question nothing else in the stack asks: an agent that has gone\n * quiet raises no intent, breaks no budget, and trips no breaker — it simply\n * disappears, and a control plane watching only for bad payments will report\n * a perfectly clean month while the work silently stopped being done. (Flash\n * lost four detectors this way, none of which announced anything.)\n *\n * Three rules hold the design honest, and each is the direct mirror of a rule\n * B1 already established:\n *\n * 1. An expectation is DECLARED, never inferred. Most agents are episodic:\n * they run when there is work. Silence is only evidence for an agent\n * somebody said should be periodic, so an unwatched agent has no liveness\n * state at all — the same rule as `taskBudget`, which never fires on an\n * intent that carries no task id.\n * 2. Silence has an AGE, not a boolean — `late` before `missing` — exactly as\n * a missing settlement is `in-flight` before it is `unsettled`.\n * 3. The engine never alarms about silence it did not witness. An engine that\n * was down cannot tell a dead agent from its own outage, so a silence\n * older than this process reads `unknown` until the process has been up\n * long enough to certify it. B1 refuses to manufacture gaps out of records\n * it cannot join; this refuses to manufacture alarms out of downtime.\n *\n * Liveness is observability and carries no authority whatsoever: it cannot\n * deny, escalate, freeze, or alter any decision. A missing agent is news for\n * a human, not an input to policy.\n */\n\n/** How the engine came to know an agent was alive. */\nexport const LivenessSource = z.enum([\n /** It submitted an intent — ANY outcome, including a denial. */\n 'intent',\n /** It reported in explicitly, having nothing to buy. */\n 'heartbeat',\n]);\nexport type LivenessSource = z.infer<typeof LivenessSource>;\n\n/** Default slack past the interval before silence becomes an alarm. */\nexport const DEFAULT_LIVENESS_GRACE_MS = 60_000;\n\n/**\n * \"This agent is supposed to check in at least this often.\"\n *\n * `graceMs` is what keeps a jittery-but-healthy agent out of the alert\n * channel: an agent on a 15m cadence that lands at 15m04s is not a dead agent,\n * and an alarm that cries at every wobble is one an operator learns to ignore\n * — which is the failure mode that loses the detector nobody was watching.\n */\nexport const LivenessExpectation = z.object({\n agentId: AgentId,\n /** Longest silence still considered normal, e.g. '15m'. */\n interval: Window,\n /** Extra slack past `interval` before the silence is an alarm. */\n graceMs: z.number().int().nonnegative().default(DEFAULT_LIVENESS_GRACE_MS),\n /**\n * When watching began. An agent that has NEVER been seen is measured from\n * here, so a watch on an agent that never starts does raise an alarm — a\n * detector that never came up is exactly as dead as one that stopped.\n */\n since: z.coerce.date(),\n /** What this agent is supposed to be doing, shown beside the alarm. */\n note: z.string().max(200).optional(),\n});\nexport type LivenessExpectation = z.infer<typeof LivenessExpectation>;\n\n/** The registration payload; the engine stamps `since`. */\nexport const LivenessWatchInput = LivenessExpectation.omit({ since: true }).extend({\n graceMs: z.number().int().nonnegative().optional(),\n});\nexport type LivenessWatchInput = z.input<typeof LivenessWatchInput>;\n\n/**\n * An out-of-band \"I am alive\" from an agent with nothing to buy.\n *\n * The engine sees every intent, so a spending agent needs no heartbeat at all.\n * This exists for the agent that is working and simply not paying for\n * anything — without it, a dead-man alarm is really a no-spend alarm, and it\n * would page a human every time an agent had a quiet afternoon.\n *\n * It authorizes nothing. The only thing a heartbeat can change is a row in the\n * liveness report; it cannot lift a breaker, a budget, or a freeze.\n */\nexport const Heartbeat = z.object({\n agentId: AgentId,\n /** When the agent was alive. Defaults to arrival time. */\n at: z.coerce.date().optional(),\n /** Free-form note from the reporter, e.g. \"poll cycle 41 ok\". */\n note: z.string().max(200).optional(),\n});\nexport type Heartbeat = z.infer<typeof Heartbeat>;\n\n/**\n * Where an agent stands against its expectation.\n *\n * - `alive` — seen within the interval.\n * - `late` — past the interval, inside the grace. Not yet news.\n * - `missing` — past both, and this process watched it happen. THE alarm.\n * - `unknown` — past both, but the engine has not been up long enough to have\n * witnessed the silence. Not an alarm: an engine that restarted\n * five minutes ago knows nothing about the last three days.\n */\nexport const LivenessStatus = z.enum(['alive', 'late', 'missing', 'unknown']);\nexport type LivenessStatus = z.infer<typeof LivenessStatus>;\n","import { z } from 'zod';\nimport { AgentId, ApproverKeyId, DecisionId, IntentId, OrgId } from './ids.js';\nimport { Asset, Chain } from './chain.js';\nimport { DecimalString } from './money.js';\n\n/**\n * Human approval of an escalated payment.\n *\n * The load-bearing rule: an approval is a SIGNATURE over the decision, made by\n * a key registered with the engine. The delivery channel (Telegram, email,\n * whatever) is transport, never authority — anyone who compromises the channel\n * gets to show a human a message, not to release funds. There is deliberately\n * no click-to-approve path.\n */\n\nexport const ApprovalVerdict = z.enum(['approve', 'reject']);\nexport type ApprovalVerdict = z.infer<typeof ApprovalVerdict>;\n\n/**\n * `pending` is the only non-terminal state. `expired` is what a TTL lapse\n * converts to — indistinguishable from `rejected` in effect (both deny), but\n * distinguished in the record so \"nobody answered\" and \"somebody said no\" stay\n * separable in an audit.\n */\nexport const ApprovalStatus = z.enum(['pending', 'approved', 'rejected', 'expired']);\nexport type ApprovalStatus = z.infer<typeof ApprovalStatus>;\n\n/**\n * A key whose signature the engine accepts as an approval. Public half only —\n * the private half lives with the human (a hardware token, a laptop, an\n * offline signer) and must never reach the engine.\n */\nexport const ApproverKey = z.object({\n id: ApproverKeyId,\n orgId: OrgId,\n name: z.string().min(1).max(200),\n /** ed25519 public key, SPKI PEM — the same encoding as the decision log's. */\n publicKey: z.string().min(1),\n algorithm: z.literal('ed25519').default('ed25519'),\n createdAt: z.coerce.date(),\n revokedAt: z.coerce.date().optional(),\n});\nexport type ApproverKey = z.infer<typeof ApproverKey>;\n\n/**\n * A parked escalation: the payment the engine refused to allow on its own\n * authority, held open until a signature arrives or the TTL lapses. Keyed by\n * the escalating decision — one decision, at most one approval request.\n *\n * The payment facts are copied in rather than referenced so the challenge a\n * human sees is self-contained: amount, vendor and resource are exactly what\n * they are being asked to authorize.\n */\nexport const ApprovalRequest = z.object({\n /** The `escalate` decision this request answers (primary key). */\n decisionId: DecisionId,\n intentId: IntentId,\n /** Binds the approval to the exact transfer — the signed bytes commit to it. */\n intentHash: z.string(),\n agentId: AgentId,\n /**\n * The org the escalating agent belongs to, stamped when the request is\n * parked. ABSENT means the agent was never registered (or the request\n * predates tenancy), and such a request is answerable only by an unscoped\n * operator — there is no org to check an approver against, and guessing one\n * is how a cross-tenant approval happens.\n */\n orgId: OrgId.optional(),\n vendorHost: z.string(),\n resource: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n /**\n * Task attribution copied from the intent. Carried so an approved payment\n * still counts against its `taskBudget` — an escalation that lost its task\n * id on the way through would make the budget silently under-count exactly\n * the payments a human had to look at.\n */\n taskId: z.string().optional(),\n /** Why policy escalated, verbatim from the decision. */\n reason: z.string(),\n /**\n * Breaker ids whose trip contributed to this escalation. An approval of\n * this request resets exactly these — moving their counting floor to the\n * approval instant — so a human waving one payment through also clears the\n * behavior that stopped it, rather than being asked again immediately.\n * Empty when ordinary rules did the escalating.\n */\n breakers: z.array(z.string()).default([]),\n status: ApprovalStatus,\n createdAt: z.coerce.date(),\n /** Past this instant the request denies, whatever arrives afterwards. */\n expiresAt: z.coerce.date(),\n resolvedAt: z.coerce.date().optional(),\n /** Which registered key signed — absent when the TTL lapsed instead. */\n approverKeyId: ApproverKeyId.optional(),\n /**\n * The follow-up decision appended to the chain when this resolved. That\n * decision, not this record, is the voucher a signer verifies: the original\n * `escalate` decision is never rewritten.\n */\n finalDecisionId: DecisionId.optional(),\n});\nexport type ApprovalRequest = z.infer<typeof ApprovalRequest>;\n\n/** The two byte-strings an approver may sign for a given request. */\nexport const ApprovalChallenges = z.object({\n approve: z.string(),\n reject: z.string(),\n});\nexport type ApprovalChallenges = z.infer<typeof ApprovalChallenges>;\n\n/**\n * A signed verdict, submitted to the engine by anyone (the signature is the\n * authority, so the carrier does not matter). `signature` is base64 ed25519\n * over `canonicalApproval({ decisionId, intentHash, verdict })`.\n */\nexport const ApprovalGrant = z.object({\n decisionId: DecisionId,\n intentHash: z.string(),\n verdict: ApprovalVerdict,\n approverKeyId: ApproverKeyId,\n signature: z.string().min(1),\n});\nexport type ApprovalGrant = z.infer<typeof ApprovalGrant>;\n\n/** Terminal statuses — a request in one of these will never change again. */\nexport function isResolved(status: ApprovalStatus): boolean {\n return status !== 'pending';\n}\n","import { z } from 'zod';\n\nexport const ReputationSubject = z.object({\n kind: z.enum(['agent', 'vendor']),\n id: z.string(),\n});\nexport type ReputationSubject = z.infer<typeof ReputationSubject>;\n\nexport const ReputationComponents = z.object({\n volume: z.number(),\n longevity: z.number(),\n disputeRate: z.number(),\n counterpartyQuality: z.number(),\n settlementReliability: z.number(),\n});\nexport type ReputationComponents = z.infer<typeof ReputationComponents>;\n\n/**\n * A reputation score for an agent or vendor (Phase 3 — Graph). `confidence` is\n * first-class: a thin-history subject returns LOW confidence rather than a\n * misleadingly high score, so policy can avoid both false trust and unfair\n * freezing.\n */\nexport const ReputationScore = z.object({\n subject: ReputationSubject,\n score: z.number().min(0).max(100),\n components: ReputationComponents,\n confidence: z.number().min(0).max(1),\n asOf: z.coerce.date(),\n /** Hash-anchored attestation, optionally published on-chain (ERC-8004). */\n evidenceUri: z.string().optional(),\n});\nexport type ReputationScore = z.infer<typeof ReputationScore>;\n","import { z } from 'zod';\nimport { AgentId, DecisionId, IntentId, SessionId } from './ids.js';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\nimport { PaymentIntent } from './intent.js';\nimport { Decision } from './decision.js';\nimport { SettledPayment } from './payment.js';\nimport { GateReceipt } from './gate-receipt.js';\nimport { ApprovalRequest } from './approval.js';\nimport { LivenessExpectation, LivenessSource } from './liveness.js';\n\n/**\n * The canonical event envelope published on the bus (NATS in production).\n * `shadow.spend` is emitted when the indexer sees on-chain spend from a managed\n * wallet with NO corresponding ALLOW decision — the bypass signal that catches\n * SDK-mode evasion and drives the upgrade to the signer tier.\n * `signature.released` / `signature.refused` are that tier's heartbeat: every\n * time the signer does or does not put a key to work, the bus knows why.\n * `gate.*` is the vendor side of the wire: every quote a Gate issues, every\n * payment it accepts, every payment it turns away.\n * `approval.*` is the human-in-the-loop tier: an escalated intent parked for a\n * signature, and the signed (or lapsed) verdict that closed it.\n * `liveness.*` is the dead-man tier (B2), and the only pair here emitted by\n * something NOT happening: a watched agent that went quiet past its expected\n * interval, and the sighting that ended the silence.\n */\nexport const ReinEvent = z.discriminatedUnion('type', [\n z.object({ type: z.literal('intent.created'), at: z.coerce.date(), intent: PaymentIntent }),\n z.object({ type: z.literal('decision.made'), at: z.coerce.date(), decision: Decision }),\n z.object({ type: z.literal('payment.settled'), at: z.coerce.date(), payment: SettledPayment }),\n z.object({\n type: z.literal('shadow.spend'),\n at: z.coerce.date(),\n agentId: AgentId,\n txHash: z.string(),\n chain: Chain,\n amount: DecimalString,\n }),\n z.object({\n type: z.literal('signature.released'),\n at: z.coerce.date(),\n sessionId: SessionId,\n agentId: AgentId,\n intentId: IntentId,\n decisionId: DecisionId,\n amount: DecimalString,\n }),\n z.object({\n type: z.literal('signature.refused'),\n at: z.coerce.date(),\n /** Refusal code, e.g. \"decision_replayed\" (see @reinconsole/signer). */\n code: z.string(),\n reason: z.string(),\n sessionId: SessionId.optional(),\n agentId: AgentId.optional(),\n intentId: IntentId.optional(),\n }),\n z.object({\n type: z.literal('gate.quoted'),\n at: z.coerce.date(),\n resource: z.string(),\n method: z.string(),\n amount: DecimalString,\n asset: z.string(),\n network: z.string(),\n }),\n z.object({ type: z.literal('gate.settled'), at: z.coerce.date(), receipt: GateReceipt }),\n z.object({\n type: z.literal('approval.requested'),\n at: z.coerce.date(),\n request: ApprovalRequest,\n }),\n z.object({\n type: z.literal('approval.resolved'),\n at: z.coerce.date(),\n request: ApprovalRequest,\n /** The follow-up allow/deny decision this resolution appended. */\n decision: Decision,\n }),\n z.object({\n type: z.literal('liveness.missing'),\n at: z.coerce.date(),\n agentId: AgentId,\n expectation: LivenessExpectation,\n /** How long the agent has been silent when the alarm was raised. */\n silentMs: z.number().int().nonnegative(),\n /** Last sighting, absent when the agent was never seen at all. */\n lastSeenAt: z.coerce.date().optional(),\n }),\n z.object({\n type: z.literal('liveness.recovered'),\n at: z.coerce.date(),\n agentId: AgentId,\n /** How long the silence lasted, measured to this sighting. */\n silentMs: z.number().int().nonnegative(),\n source: LivenessSource,\n }),\n z.object({\n type: z.literal('gate.refused'),\n at: z.coerce.date(),\n /** Refusal code, e.g. \"payment_replayed\" (see @reinconsole/gate). */\n code: z.string(),\n reason: z.string(),\n resource: z.string(),\n /** Known only when the payment header decoded far enough to name a payer. */\n payer: z.string().optional(),\n }),\n]);\nexport type ReinEvent = z.infer<typeof ReinEvent>;\n","import { formatErc8004Id, parseErc8004Id, sumDecimal, type ReputationSubject } from '@reinconsole/core';\n\n/** A value that may be produced synchronously or awaited (durable writes). */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** One settled-money edge to a counterparty (the \"graph\" in reputation graph). */\nexport interface CounterpartyLine {\n settled: number;\n /** Settled decimal volume across this edge. */\n volume: string;\n}\n\n/**\n * Everything the graph has observed about one subject. Counters are raw and\n * append-only — scoring (scoring.ts) is a pure function over this record, so a\n * score is always re-derivable and explainable from the evidence behind it.\n */\nexport interface SubjectEvidence {\n subject: ReputationSubject;\n firstSeenMs: number;\n lastSeenMs: number;\n /** Payment attempts observed (allowed intents, presented payments). */\n attempts: number;\n /** Confirmed settlements (indexer-reconciled or gate-receipted). */\n settled: number;\n /** Settled decimal volume. */\n volume: string;\n /** Refusals by code (gate refusal codes, signer refusal codes). */\n refusals: Record<string, number>;\n /** Managed-wallet spend with no ALLOW decision behind it (bypass). */\n shadowSpends: number;\n /** Manual out-of-band reports (chargebacks, fraud complaints). */\n disputes: number;\n /** Manual out-of-band vouches. */\n endorsements: number;\n /** Who this subject has settled money with, keyed by subjectKey(). */\n counterparties: Map<string, CounterpartyLine>;\n}\n\n/**\n * Canonical subject identity. Hosts and EVM addresses are case-insensitive,\n * so they normalize to lowercase; ULID agent ids are case-sensitive and pass\n * through. ERC-8004 ids (`eip155:{chainId}:{registry}/{tokenId}`) normalize\n * via parse->format — @reinconsole/erc8004 always emits the canonical form, but this\n * is the graph's OWN boundary (remote producers hit POST /v1/links with\n * hand-built strings), and a checksummed registry or zero-padded tokenId\n * variant must not mint a split subject row. Note the two id spaces under\n * kind \"agent\": the engine names agents by ULID, a gate names them by paying\n * wallet — ERC-8004 linking (S17) folds both into the on-chain identity.\n */\nexport function normalizeSubject(subject: ReputationSubject): ReputationSubject {\n if (subject.id.startsWith('eip155:')) {\n const ref = parseErc8004Id(subject.id);\n if (ref) return { kind: subject.kind, id: formatErc8004Id(ref) };\n // Malformed eip155-ish strings fall through to the plain rules below.\n }\n const id =\n subject.kind === 'vendor' || subject.id.startsWith('0x') || subject.id.startsWith('0X')\n ? subject.id.toLowerCase()\n : subject.id;\n return { kind: subject.kind, id };\n}\n\nexport function subjectKey(subject: ReputationSubject): string {\n const normal = normalizeSubject(subject);\n return `${normal.kind}:${normal.id}`;\n}\n\n/**\n * The evidence store seam the graph depends on. Writes may be asynchronous (a\n * durable ledger persists behind them); reads are synchronous from the\n * hydrated working set so `score()` / `payerCheck()` stay sync in the gate hot\n * path. Mutations are explicit, atomic operations rather than a returned record\n * the caller mutates — that is what lets a durable implementation persist them.\n */\nexport interface EvidenceLedgerPort {\n /** A payment attempt (allowed intent / presented payment). */\n recordAttempt(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /** A confirmed settlement between two subjects (credits both sides + the edge). */\n recordSettlement(\n a: ReputationSubject,\n b: ReputationSubject,\n amount: string,\n atMs: number,\n ): MaybePromise<void>;\n /** A refusal against a subject, by code. */\n recordRefusal(subject: ReputationSubject, code: string, atMs: number): MaybePromise<void>;\n /** Managed-wallet spend with no ALLOW behind it. */\n recordShadowSpend(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /** An out-of-band dispute. */\n recordDispute(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /** An out-of-band endorsement. */\n recordEndorsement(subject: ReputationSubject, atMs: number): MaybePromise<void>;\n /**\n * Fold everything known about `alias` into `canonical` and forget the alias:\n * counters sum, volumes add, first/last seen widen, and settled-money edges\n * re-key on BOTH endpoints (peers that pointed at the alias now point at the\n * canonical subject). No-op when the alias has no record — which makes\n * re-asserting links at boot idempotent. This is the identity-linking\n * primitive (ERC-8004: one on-chain identity, many addresses/ids).\n */\n merge(canonical: ReputationSubject, alias: ReputationSubject): MaybePromise<void>;\n get(subject: ReputationSubject): SubjectEvidence | undefined;\n getByKey(key: string): SubjectEvidence | undefined;\n all(): IterableIterator<SubjectEvidence>;\n readonly size: number;\n /**\n * Await any pending durable writes (no-op in memory). Durable\n * implementations surface write failures HERE — the graph fire-and-forgets\n * its writes (a bus handler cannot await), so flush() is the error channel.\n */\n flush?(): Promise<void>;\n}\n\n/** The evidence store: one record per subject, created on first sighting. */\nexport class EvidenceLedger implements EvidenceLedgerPort {\n private readonly records = new Map<string, SubjectEvidence>();\n\n recordAttempt(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).attempts += 1;\n }\n\n recordSettlement(\n a: ReputationSubject,\n b: ReputationSubject,\n amount: string,\n atMs: number,\n ): void {\n for (const [subject, other] of [\n [a, b],\n [b, a],\n ] as const) {\n const ev = this.touch(subject, atMs);\n ev.settled += 1;\n ev.volume = sumDecimal([ev.volume, amount]);\n const key = subjectKey(other);\n const line = ev.counterparties.get(key) ?? { settled: 0, volume: '0' };\n line.settled += 1;\n line.volume = sumDecimal([line.volume, amount]);\n ev.counterparties.set(key, line);\n }\n }\n\n recordRefusal(subject: ReputationSubject, code: string, atMs: number): void {\n const ev = this.touch(subject, atMs);\n ev.refusals[code] = (ev.refusals[code] ?? 0) + 1;\n }\n\n recordShadowSpend(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).shadowSpends += 1;\n }\n\n recordDispute(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).disputes += 1;\n }\n\n recordEndorsement(subject: ReputationSubject, atMs: number): void {\n this.touch(subject, atMs).endorsements += 1;\n }\n\n merge(canonical: ReputationSubject, alias: ReputationSubject): void {\n const aliasKey = subjectKey(alias);\n const canonicalKey = subjectKey(canonical);\n const from = this.records.get(aliasKey);\n if (!from || aliasKey === canonicalKey) return;\n // The canonical side may have no record yet — the alias's history becomes\n // its history (same create shape as touch, seeded with the alias's window).\n const into = this.records.get(canonicalKey) ?? this.touch(canonical, from.firstSeenMs);\n into.firstSeenMs = Math.min(into.firstSeenMs, from.firstSeenMs);\n into.lastSeenMs = Math.max(into.lastSeenMs, from.lastSeenMs);\n into.attempts += from.attempts;\n into.settled += from.settled;\n into.volume = sumDecimal([into.volume, from.volume]);\n for (const [code, count] of Object.entries(from.refusals)) {\n into.refusals[code] = (into.refusals[code] ?? 0) + count;\n }\n into.shadowSpends += from.shadowSpends;\n into.disputes += from.disputes;\n into.endorsements += from.endorsements;\n for (const [peerKey, line] of from.counterparties) {\n if (peerKey === canonicalKey) continue; // a self-edge would score the subject by itself\n const existing = into.counterparties.get(peerKey) ?? { settled: 0, volume: '0' };\n existing.settled += line.settled;\n existing.volume = sumDecimal([existing.volume, line.volume]);\n into.counterparties.set(peerKey, existing);\n // Re-key the peer's reverse edge: alias -> canonical.\n const peer = this.records.get(peerKey);\n const held = peer?.counterparties.get(aliasKey);\n if (peer && held) {\n peer.counterparties.delete(aliasKey);\n const reverse = peer.counterparties.get(canonicalKey) ?? { settled: 0, volume: '0' };\n reverse.settled += held.settled;\n reverse.volume = sumDecimal([reverse.volume, held.volume]);\n peer.counterparties.set(canonicalKey, reverse);\n }\n }\n into.counterparties.delete(aliasKey);\n this.records.delete(aliasKey);\n }\n\n get(subject: ReputationSubject): SubjectEvidence | undefined {\n return this.records.get(subjectKey(subject));\n }\n\n getByKey(key: string): SubjectEvidence | undefined {\n return this.records.get(key);\n }\n\n all(): IterableIterator<SubjectEvidence> {\n return this.records.values();\n }\n\n get size(): number {\n return this.records.size;\n }\n\n /**\n * Insert a fully-formed record verbatim — the hydration primitive a durable\n * store uses to rebuild the working set on open. Bypasses the live counters\n * (the record already holds its accumulated state and timestamps).\n */\n load(record: SubjectEvidence): void {\n this.records.set(subjectKey(record.subject), record);\n }\n\n /** Get-or-create the record for a subject and stamp first/last seen. */\n private touch(subject: ReputationSubject, atMs: number): SubjectEvidence {\n const key = subjectKey(subject);\n let record = this.records.get(key);\n if (!record) {\n record = {\n subject: normalizeSubject(subject),\n firstSeenMs: atMs,\n lastSeenMs: atMs,\n attempts: 0,\n settled: 0,\n volume: '0',\n refusals: {},\n shadowSpends: 0,\n disputes: 0,\n endorsements: 0,\n counterparties: new Map(),\n };\n this.records.set(key, record);\n }\n if (atMs < record.firstSeenMs) record.firstSeenMs = atMs;\n if (atMs > record.lastSeenMs) record.lastSeenMs = atMs;\n return record;\n }\n}\n","import type { MaybePromise } from './evidence.js';\n\n/**\n * What an `intent.created` event tells us, kept until the matching\n * `payment.settled` (which carries only an intentId) can be attributed.\n */\nexport interface IntentFacts {\n agentId: string;\n host: string;\n amount: string;\n}\n\nexport const DEFAULT_CORRELATION_LIMIT = 10_000;\n\n/**\n * Bridges `intent.created` -> later `payment.settled`/`decision.made`. A\n * `payment.settled` event names only an intentId, so the graph remembers every\n * undecided intent until it settles. Bounded (oldest-evicted) so undecided\n * intents that never settle cannot leak. Writes may be async (a durable store\n * persists them so an in-flight intent survives a restart); reads are sync.\n */\nexport interface IntentCorrelationPort {\n remember(intentId: string, facts: IntentFacts): MaybePromise<void>;\n /** Read the facts without consuming them (decision.made reads, doesn't burn). */\n peek(intentId: string): IntentFacts | undefined;\n /** Read and consume (payment.settled attributes once, then forgets). */\n take(intentId: string): IntentFacts | undefined;\n readonly size: number;\n /**\n * Await any pending durable writes (no-op in memory). Durable\n * implementations surface write failures HERE — the graph fire-and-forgets\n * its writes, so flush() is the error channel.\n */\n flush?(): Promise<void>;\n}\n\n/** In-memory correlation map with FIFO eviction once `limit` is reached. */\nexport class InMemoryIntentStore implements IntentCorrelationPort {\n private readonly intents = new Map<string, IntentFacts>();\n\n constructor(private readonly limit: number = DEFAULT_CORRELATION_LIMIT) {}\n\n remember(intentId: string, facts: IntentFacts): void {\n // Re-remembering an existing id is an update, not a new entry — evicting\n // for it would shrink the working set below the bound (and desync a\n // durable mirror, whose upsert adds no row). Updates keep their position,\n // matching an SQL upsert leaving seq unchanged.\n if (!this.intents.has(intentId) && this.intents.size >= this.limit) {\n const oldest = this.intents.keys().next().value;\n if (oldest !== undefined) this.intents.delete(oldest);\n }\n this.intents.set(intentId, facts);\n }\n\n peek(intentId: string): IntentFacts | undefined {\n return this.intents.get(intentId);\n }\n\n take(intentId: string): IntentFacts | undefined {\n const facts = this.intents.get(intentId);\n if (facts) this.intents.delete(intentId);\n return facts;\n }\n\n get size(): number {\n return this.intents.size;\n }\n}\n","import type { ReputationComponents } from '@reinconsole/core';\nimport type { SubjectEvidence } from './evidence.js';\n\n/**\n * Component weights, summing to 1. The blend is a deliberately transparent\n * heuristic: every component is 0–100 with higher = healthier, so a score is\n * readable straight off its explanation. Sophistication can replace this\n * later without touching the evidence model — scores are pure functions of\n * evidence, never stored.\n */\nexport interface ScoreWeights {\n volume: number;\n longevity: number;\n disputeRate: number;\n counterpartyQuality: number;\n settlementReliability: number;\n}\n\nexport const DEFAULT_WEIGHTS: ScoreWeights = {\n settlementReliability: 0.3,\n disputeRate: 0.3,\n volume: 0.15,\n counterpartyQuality: 0.15,\n longevity: 0.1,\n};\n\nconst DAY_MS = 86_400_000;\n\n/**\n * How much one bad observation hurts, relative to a plain refusal. Disputes\n * and shadow spends are deliberate misconduct; a replay means someone tried\n * to spend the same money twice; the rest is sloppiness.\n */\nconst BADNESS = { dispute: 4, shadowSpend: 4, replay: 3, refusal: 1, endorsementCredit: 2 };\n\nconst REPLAY_CODES = new Set(['payment_replayed', 'decision_replayed']);\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.min(hi, Math.max(lo, value));\n}\n\nfunction refusalCounts(ev: SubjectEvidence): { replays: number; others: number } {\n let replays = 0;\n let others = 0;\n for (const [code, count] of Object.entries(ev.refusals)) {\n if (REPLAY_CODES.has(code)) replays += count;\n else others += count;\n }\n return { replays, others };\n}\n\n/** Saturating settled-transaction volume: 0 at none, ~63 at 20, ~92 at 50. */\nexport function volumeComponent(ev: SubjectEvidence): number {\n return 100 * (1 - Math.exp(-ev.settled / 20));\n}\n\n/** How long the subject has been known: linear to 100 at 30 days. */\nexport function longevityComponent(ev: SubjectEvidence, nowMs: number): number {\n const knownDays = Math.max(0, nowMs - ev.firstSeenMs) / DAY_MS;\n return 100 * Math.min(1, knownDays / 30);\n}\n\n/**\n * Dispute hygiene (the core field is named `disputeRate`; the component is\n * stored INVERTED — 100 = a clean record — so all five components blend in\n * the same direction). Weighted badness over total interactions; manual\n * endorsements buy back a little slack.\n */\nexport function disputeComponent(ev: SubjectEvidence): number {\n const { replays, others } = refusalCounts(ev);\n const badness = Math.max(\n 0,\n ev.disputes * BADNESS.dispute +\n ev.shadowSpends * BADNESS.shadowSpend +\n replays * BADNESS.replay +\n others * BADNESS.refusal -\n ev.endorsements * BADNESS.endorsementCredit,\n );\n const interactions = Math.max(1, ev.attempts + ev.disputes + ev.endorsements);\n return 100 * Math.max(0, 1 - badness / interactions);\n}\n\n/** Settled / attempted. No attempts yet = neutral 50, not perfect 100. */\nexport function reliabilityComponent(ev: SubjectEvidence): number {\n if (ev.attempts === 0) return 50;\n return 100 * Math.min(1, ev.settled / ev.attempts);\n}\n\n/**\n * Confidence is first-class (see core ReputationScore): a thin or brand-new\n * history yields LOW confidence rather than a misleading score, so consumers\n * (engine sync, gate screening) can refuse to act on it. Depth of evidence\n * saturates around 10 observations; age stops mattering after a week.\n *\n * The 0.1 age floor is load-bearing for fairness. Both consumers act at a 0.3\n * confidence floor (`syncVendors`, `payerCheck`), so capping a day-0 subject at\n * 0.1 makes first-day enforcement impossible BY CONSTRUCTION rather than by\n * arithmetic luck. It used to be 0.4, which left day-0 subjects enforceable\n * after only 14 observations — `depth` saturates so fast that a busy vendor\n * cleared that in seconds, and the console's own vendor sat at 0.30136 against\n * the 0.3 floor, i.e. inside the newcomer grace period by 0.0014. Enforcement\n * now needs roughly 1.6 days (high volume) to 2.3 days (moderate) of history:\n * a grace period measured in time, which is what \"newcomer\" means.\n *\n * Note the constant only ever applies to subjects younger than a week — past\n * 7 days `min(1, d/7)` saturates and age is 1.0 under any floor — so lowering\n * it leaves every mature score bit-for-bit unchanged.\n */\nexport function confidence(ev: SubjectEvidence, nowMs: number): number {\n const { replays, others } = refusalCounts(ev);\n const observations =\n ev.attempts + ev.disputes + ev.endorsements + ev.shadowSpends + replays + others;\n const depth = 1 - Math.exp(-observations / 10);\n const knownDays = Math.max(0, nowMs - ev.firstSeenMs) / DAY_MS;\n const age = 0.1 + 0.9 * Math.min(1, knownDays / 7);\n return clamp(depth * age, 0, 1);\n}\n\n/** Blend all five components into the headline 0–100 score. */\nexport function blend(components: ReputationComponents, weights: ScoreWeights): number {\n const total =\n components.volume * weights.volume +\n components.longevity * weights.longevity +\n components.disputeRate * weights.disputeRate +\n components.counterpartyQuality * weights.counterpartyQuality +\n components.settlementReliability * weights.settlementReliability;\n return clamp(Math.round(total), 0, 100);\n}\n\n/**\n * The one-hop base score: the blend WITHOUT counterpartyQuality, with the\n * remaining weights renormalized. Counterparty quality for subject A averages\n * the base scores of A's counterparties — base, not full, so the computation\n * is a single hop and cycles (A pays B pays A) cannot recurse.\n */\nexport function blendBase(ev: SubjectEvidence, nowMs: number, weights: ScoreWeights): number {\n const remaining =\n weights.volume + weights.longevity + weights.disputeRate + weights.settlementReliability;\n if (remaining <= 0) return 50;\n const total =\n volumeComponent(ev) * weights.volume +\n longevityComponent(ev, nowMs) * weights.longevity +\n disputeComponent(ev) * weights.disputeRate +\n reliabilityComponent(ev) * weights.settlementReliability;\n return clamp(total / remaining, 0, 100);\n}\n","import {\n ReputationScore,\n type Receipt,\n type ReinEvent,\n type ReputationComponents,\n type ReputationSubject,\n} from '@reinconsole/core';\nimport {\n EvidenceLedger,\n normalizeSubject,\n subjectKey,\n type EvidenceLedgerPort,\n type MaybePromise,\n type SubjectEvidence,\n} from './evidence.js';\nimport {\n DEFAULT_CORRELATION_LIMIT,\n InMemoryIntentStore,\n type IntentCorrelationPort,\n} from './intents.js';\nimport {\n blend,\n blendBase,\n confidence,\n DEFAULT_WEIGHTS,\n disputeComponent,\n longevityComponent,\n reliabilityComponent,\n volumeComponent,\n type ScoreWeights,\n} from './scoring.js';\n\n/** Anything with a Rein event bus: engine, indexer, gate, signer. */\nexport interface EventSource {\n onEvent(handler: (event: ReinEvent) => void): void;\n}\n\n/**\n * Gate refusal codes that are the vendor's throttle or the vendor's rails\n * failing — not payer misbehavior. They must never become reputation evidence\n * (see the `gate.refused` case below). Mirrors @reinconsole/gate's no-fault classes;\n * kept as strings because the event schema deliberately carries codes as\n * strings (remote gates may run other versions).\n */\nconst NO_FAULT_GATE_CODES = new Set([\n 'rate_limited',\n 'velocity_exceeded',\n 'rails_unavailable',\n 'settle_unknown',\n 'price_unavailable',\n 'price_ceiling',\n]);\n\n/**\n * Where vendor scores land. `PolicyEngine.spend` satisfies this structurally,\n * so `graph.syncVendors(engine.spend)` closes the loop — durable when the\n * engine runs on @reinconsole/store.\n */\nexport interface VendorReputationSink {\n setVendorReputation(host: string, score: number): void | Promise<void>;\n}\n\nexport interface ReputationGraphOptions {\n weights?: Partial<ScoreWeights>;\n /** Injectable clock (scores are functions of evidence AND time). */\n now?: () => Date;\n /**\n * Max undecided intents remembered for settlement correlation. Ignored when\n * a custom `intents` store is injected (that store owns its own bound).\n */\n correlationLimit?: number;\n /**\n * Durable evidence ledger. Defaults to in-memory; @reinconsole/store provides a\n * PGlite-backed one so scores survive a restart.\n */\n ledger?: EvidenceLedgerPort;\n /** Durable intent correlation store. Defaults to in-memory (bounded by `correlationLimit`). */\n intents?: IntentCorrelationPort;\n}\n\nexport interface ManualReport {\n subject: ReputationSubject;\n kind: 'dispute' | 'endorsement';\n /** When the incident happened (defaults to now). */\n at?: Date;\n note?: string;\n}\n\nexport interface SyncedVendorScore {\n host: string;\n score: number;\n confidence: number;\n}\n\nexport interface SyncOptions {\n /**\n * Scores below this confidence are NOT pushed — the evaluator treats an\n * unknown reputation as indeterminate (never triggers vendorReputationLt),\n * and a thin history must stay unknown rather than condemn a newcomer.\n */\n minConfidence?: number;\n}\n\n/** A score plus the raw evidence it was derived from, render-ready. */\nexport interface ReputationExplanation {\n score: ReputationScore;\n weights: ScoreWeights;\n evidence: {\n attempts: number;\n settled: number;\n volume: string;\n refusals: Record<string, number>;\n shadowSpends: number;\n disputes: number;\n endorsements: number;\n firstSeen: Date;\n lastSeen: Date;\n counterparties: { subject: ReputationSubject; settled: number; volume: string }[];\n };\n}\n\n/**\n * The Rein reputation graph (Phase 3). Subscribes to the event buses both\n * sides already publish — the engine/indexer name vendors by host and agents\n * by id; a gate names payers by wallet — and accumulates per-subject evidence:\n * settlements, refusals, shadow spends, manual disputes, and settled-money\n * edges between counterparties. Scores are computed on demand (never stored)\n * from that evidence, so every number is explainable, and they feed back into\n * enforcement on both sides of the wire:\n *\n * graph.syncVendors(engine.spend) -> policies with vendorReputationLt fire\n * screen: { check: payerCheck(graph) } -> a gate turns away low-rep wallets\n *\n * Event mapping notes: engine-side evidence keys agents by ULID and vendors by\n * host; gate-side evidence keys payers by wallet and recipients by payTo\n * address — distinct subjects, so feeding one graph from BOTH buses (the\n * console world) never double-counts. `payment.settled` carries only an\n * intentId, so the graph keeps a bounded intent.created correlation map.\n * Denied decisions are deliberately NOT held against anyone: a deny means\n * policy worked, not that the agent or vendor misbehaved.\n */\nexport class ReputationGraph {\n private readonly ledger: EvidenceLedgerPort;\n private readonly intents: IntentCorrelationPort;\n private readonly weights: ScoreWeights;\n private readonly now: () => Date;\n /** aliasKey -> canonical subject. Links are derived state (an agent registry\n * or ERC-8004 lookup knows them) — re-assert at boot; merges are idempotent. */\n private readonly aliases = new Map<string, ReputationSubject>();\n\n constructor(options: ReputationGraphOptions = {}) {\n this.weights = { ...DEFAULT_WEIGHTS, ...options.weights };\n this.now = options.now ?? (() => new Date());\n this.ledger = options.ledger ?? new EvidenceLedger();\n this.intents =\n options.intents ??\n new InMemoryIntentStore(options.correlationLimit ?? DEFAULT_CORRELATION_LIMIT);\n }\n\n /** Subscribe to a bus. Returns `this` so construction chains. */\n observe(source: EventSource): this {\n source.onEvent((event) => this.ingest(event));\n return this;\n }\n\n /**\n * Fire-and-forget a port write: bus handlers cannot await, and a rejecting\n * durable write must never become an unhandled rejection (Node kills the\n * process). Failures are the port's to surface — flush() throws them.\n */\n private fire(write: () => MaybePromise<void>): void {\n // Thunk form (parity with the gate's fire, S19): a ledger that THROWS\n // synchronously must not turn an ingest into a crash — telemetry failures\n // are the port's to surface, and flush() throws them.\n try {\n void Promise.resolve(write()).catch(() => undefined);\n } catch {\n // swallowed by design — see above\n }\n }\n\n /**\n * Declare that `alias` is the same real-world party as `canonical` (the\n * ERC-8004 identity story: one identity, many addresses/ids — an engine\n * agent's ULID and its paying wallet, or a vendor's host and its payTo\n * address). Everything already known about the alias is folded into the\n * canonical subject, and all future evidence and lookups for the alias\n * resolve to it. Idempotent — re-asserting known links at boot is free.\n */\n link(canonical: ReputationSubject, alias: ReputationSubject): void {\n const target = normalizeSubject(this.resolve(canonical)); // flatten chains\n const aliasKey = subjectKey(alias);\n if (subjectKey(target) === aliasKey) return;\n this.aliases.set(aliasKey, target);\n // Repoint any alias that resolved THROUGH this alias (a->b then b->c).\n for (const [key, resolved] of this.aliases) {\n if (subjectKey(resolved) === aliasKey) this.aliases.set(key, target);\n }\n this.fire(() => this.ledger.merge(target, alias));\n }\n\n /** The canonical subject for a possibly-aliased one. */\n private resolve(subject: ReputationSubject): ReputationSubject {\n return this.aliases.get(subjectKey(subject)) ?? subject;\n }\n\n ingest(event: ReinEvent): void {\n const atMs = event.at.getTime();\n switch (event.type) {\n case 'intent.created': {\n this.fire(() =>\n this.intents.remember(event.intent.id, {\n agentId: event.intent.agentId,\n host: event.intent.vendor.host,\n amount: event.intent.amount,\n }),\n );\n return;\n }\n case 'decision.made': {\n if (event.decision.outcome !== 'allow') return;\n const facts = this.intents.peek(event.decision.intentId);\n if (!facts) return;\n this.fire(() => this.ledger.recordAttempt(this.resolve({ kind: 'agent', id: facts.agentId }), atMs));\n this.fire(() => this.ledger.recordAttempt(this.resolve({ kind: 'vendor', id: facts.host }), atMs));\n return;\n }\n case 'payment.settled': {\n const facts = this.intents.take(event.payment.intentId);\n if (!facts) return; // unattributable — no subject to credit\n const agent = this.resolve({ kind: 'agent', id: facts.agentId });\n const vendor = this.resolve({ kind: 'vendor', id: facts.host });\n this.fire(() => this.ledger.recordSettlement(agent, vendor, facts.amount, atMs));\n return;\n }\n case 'shadow.spend': {\n this.fire(() => this.ledger.recordShadowSpend(this.resolve({ kind: 'agent', id: event.agentId }), atMs));\n return;\n }\n case 'signature.refused': {\n const agentId = event.agentId; // hoisted: narrowing must survive the thunk\n if (!agentId) return;\n this.fire(() =>\n this.ledger.recordRefusal(this.resolve({ kind: 'agent', id: agentId }), event.code, atMs),\n );\n return;\n }\n case 'gate.settled': {\n const payer = this.resolve({ kind: 'agent', id: event.receipt.payer });\n const recipient = this.resolve({ kind: 'vendor', id: event.receipt.payTo });\n this.fire(() => this.ledger.recordAttempt(payer, atMs));\n this.fire(() => this.ledger.recordAttempt(recipient, atMs));\n this.fire(() => this.ledger.recordSettlement(payer, recipient, event.receipt.amount, atMs));\n return;\n }\n case 'gate.refused': {\n // No-fault refusals carry NO evidence — the gate-side parallel of\n // \"denied decisions count against no one\". Throttle codes are the\n // VENDOR'S cap (one gate's rate limit must not bleed into a payer's\n // global score); rails codes are the vendor's infrastructure failing\n // (a settle_unknown payment may even have gone through). Not even the\n // attempt is recorded: an attempt with no settlement would silently\n // drag settlementReliability down.\n if (!event.payer || NO_FAULT_GATE_CODES.has(event.code)) return;\n const payer = this.resolve({ kind: 'agent', id: event.payer });\n this.fire(() => this.ledger.recordAttempt(payer, atMs));\n this.fire(() => this.ledger.recordRefusal(payer, event.code, atMs));\n return;\n }\n // signature.released and gate.quoted carry no evidence the engine-side\n // events don't already: released duplicates the allow that preceded it,\n // and a quote names no payer.\n case 'signature.released':\n case 'gate.quoted':\n return;\n }\n }\n\n /**\n * Agent-side alternative to observing the engine bus: feed the SDK guard's\n * onReceipt straight in. Connect events OR receipts per side, not both —\n * a receipt restates the decision/settlement the bus already carried.\n */\n ingestReceipt(receipt: Receipt): void {\n if (receipt.outcome !== 'allow') return; // policy verdicts are not subject badness\n const atMs = receipt.createdAt.getTime();\n const agent = this.resolve({ kind: 'agent', id: receipt.agentId });\n const vendor = this.resolve({ kind: 'vendor', id: receipt.vendorHost });\n this.fire(() => this.ledger.recordAttempt(agent, atMs));\n this.fire(() => this.ledger.recordAttempt(vendor, atMs));\n if (receipt.settlement?.txHash) {\n this.fire(() => this.ledger.recordSettlement(agent, vendor, receipt.amount, atMs));\n }\n }\n\n /** Record an out-of-band dispute or endorsement against a subject. */\n report(input: ManualReport): void {\n const atMs = (input.at ?? this.now()).getTime();\n const subject = this.resolve(input.subject);\n if (input.kind === 'dispute') this.fire(() => this.ledger.recordDispute(subject, atMs));\n else this.fire(() => this.ledger.recordEndorsement(subject, atMs));\n }\n\n /** Await any pending durable writes (no-op for in-memory stores). */\n async flush(): Promise<void> {\n await this.ledger.flush?.();\n await this.intents.flush?.();\n }\n\n /** The score for one subject, or undefined when nothing is known (fairness).\n * Aliased subjects resolve to their canonical identity — a linked wallet\n * answers with the merged history, on both sides of the wire. */\n score(subject: ReputationSubject): ReputationScore | undefined {\n const ev = this.ledger.get(this.resolve(subject));\n return ev && this.scoreOf(ev);\n }\n\n /** All known scores, best first. */\n scores(kind?: ReputationSubject['kind']): ReputationScore[] {\n const out: ReputationScore[] = [];\n for (const ev of this.ledger.all()) {\n if (kind && ev.subject.kind !== kind) continue;\n out.push(this.scoreOf(ev));\n }\n return out.sort((a, b) => b.score - a.score);\n }\n\n /** The score plus the raw evidence behind it. */\n explain(subject: ReputationSubject): ReputationExplanation | undefined {\n const ev = this.ledger.get(this.resolve(subject));\n if (!ev) return undefined;\n return {\n score: this.scoreOf(ev),\n weights: this.weights,\n evidence: {\n attempts: ev.attempts,\n settled: ev.settled,\n volume: ev.volume,\n refusals: { ...ev.refusals },\n shadowSpends: ev.shadowSpends,\n disputes: ev.disputes,\n endorsements: ev.endorsements,\n firstSeen: new Date(ev.firstSeenMs),\n lastSeen: new Date(ev.lastSeenMs),\n counterparties: [...ev.counterparties.entries()].map(([key, line]) => {\n const [kind, ...id] = key.split(':');\n return {\n subject: { kind: kind as ReputationSubject['kind'], id: id.join(':') },\n settled: line.settled,\n volume: line.volume,\n };\n }),\n },\n };\n }\n\n /**\n * Push every vendor score that clears the confidence floor into the engine's\n * spend store (or any sink). Returns what was pushed. Low-confidence scores\n * are withheld so `vendorReputationLt` keeps treating thin histories as\n * unknown — the evaluator's no-data-no-trigger rule stays intact end to end.\n */\n async syncVendors(\n sink: VendorReputationSink,\n options: SyncOptions = {},\n ): Promise<SyncedVendorScore[]> {\n const minConfidence = options.minConfidence ?? 0.3;\n const pushed: SyncedVendorScore[] = [];\n for (const ev of this.ledger.all()) {\n if (ev.subject.kind !== 'vendor') continue;\n const score = this.scoreOf(ev);\n if (score.confidence < minConfidence) continue;\n await sink.setVendorReputation(ev.subject.id, score.score);\n pushed.push({ host: ev.subject.id, score: score.score, confidence: score.confidence });\n }\n return pushed;\n }\n\n /** Number of subjects with evidence. */\n subjects(): number {\n return this.ledger.size;\n }\n\n private scoreOf(ev: SubjectEvidence): ReputationScore {\n const nowMs = this.now().getTime();\n const components: ReputationComponents = {\n volume: volumeComponent(ev),\n longevity: longevityComponent(ev, nowMs),\n disputeRate: disputeComponent(ev),\n counterpartyQuality: this.counterpartyQuality(ev, nowMs),\n settlementReliability: reliabilityComponent(ev),\n };\n return ReputationScore.parse({\n subject: ev.subject,\n score: blend(components, this.weights),\n components,\n confidence: confidence(ev, nowMs),\n asOf: new Date(nowMs),\n });\n }\n\n /** One-hop mean of the counterparties' base scores; neutral 50 when alone. */\n private counterpartyQuality(ev: SubjectEvidence, nowMs: number): number {\n if (ev.counterparties.size === 0) return 50;\n let total = 0;\n for (const key of ev.counterparties.keys()) {\n const other = this.ledger.getByKey(key);\n total += other ? blendBase(other, nowMs, this.weights) : 50;\n }\n return total / ev.counterparties.size;\n }\n}\n\nexport interface PayerCheckOptions {\n /** Refuse payers scoring below this (default 40). */\n denyBelow?: number;\n /** Ignore scores below this confidence (default 0.3) — newcomers pass. */\n minConfidence?: number;\n}\n\n/**\n * Reputation-driven gate screening: plugs into @reinconsole/gate's `screen.check`.\n * Unknown wallets and thin histories pass (same fairness rule as the engine\n * sync); a confident low score is turned away at the door, before any\n * facilitator round-trip.\n */\nexport function payerCheck(\n graph: ReputationGraph,\n options: PayerCheckOptions = {},\n): (payer: string) => string | undefined {\n const denyBelow = options.denyBelow ?? 40;\n const minConfidence = options.minConfidence ?? 0.3;\n return (payer) => {\n const score = graph.score({ kind: 'agent', id: payer });\n if (!score || score.confidence < minConfidence) return undefined;\n if (score.score >= denyBelow) return undefined;\n return `payer reputation ${score.score} is below this gate's floor of ${denyBelow}`;\n };\n}\n","import { fileURLToPath } from 'node:url';\nimport { realpathSync } from 'node:fs';\nimport Fastify, { type FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport { ReinEvent, ReputationSubject } from '@reinconsole/core';\nimport { ApiKeyAuth, AuthError } from '@reinconsole/core/auth';\nimport { ReputationGraph } from './graph.js';\n\nconst ReportInput = z.object({\n subject: ReputationSubject,\n kind: z.enum(['dispute', 'endorsement']),\n at: z.coerce.date().optional(),\n note: z.string().max(500).optional(),\n});\n\nconst ScoresQuery = z.object({ kind: z.enum(['agent', 'vendor']).optional() });\n\nconst LinkInput = z\n .object({\n canonical: ReputationSubject,\n alias: ReputationSubject,\n })\n // Merging across kinds would delete a vendor row into an agent identity\n // (silently dropping the host from syncVendors) — always a caller bug.\n .refine((l) => l.canonical.kind === l.alias.kind, {\n message: 'canonical and alias must be the same kind',\n });\n\nexport interface GraphServerOptions {\n /**\n * API-key authentication for the WRITE routes. Omit for embedded use (the\n * console world holds the graph object directly) or for a loopback-only\n * process; pass an {@link ApiKeyAuth} and every write demands a `report`\n * scope while reads stay open.\n */\n auth?: ApiKeyAuth;\n}\n\n/** Writes accept evidence; reads only reflect it back. */\nfunction isWrite(method: string): boolean {\n return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS';\n}\n\n/**\n * Build the graph HTTP API. Remote producers POST their events here; anyone\n * can read scores with the evidence behind them. Pass a graph for tests, or\n * let it create a fresh in-memory one.\n *\n * READS ARE OPEN BY DESIGN and that is the whole point of a reputation graph:\n * a score nobody can read governs nothing. WRITES are the asymmetry — an event\n * posted here becomes evidence about a subject that did not post it — so when\n * `auth` is supplied they require the `report` scope. Without `auth` the server\n * is exactly as open as it always was, which is why the bins that expose it\n * refuse a public bind unless a key exists or the operator says otherwise.\n */\nexport function buildGraphServer(\n graph: ReputationGraph = new ReputationGraph(),\n options: GraphServerOptions = {},\n): FastifyInstance {\n const app = Fastify({ logger: false });\n const auth = options.auth;\n\n // Turn ZodErrors into clean 400s instead of 500s.\n app.setErrorHandler((err: unknown, _req, reply) => {\n if (err instanceof z.ZodError) {\n return reply.status(400).send({ error: 'validation_error', issues: err.issues });\n }\n if (AuthError.is(err)) {\n return reply.status(err.status).send({ error: err.code, message: err.message });\n }\n const message = err instanceof Error ? err.message : String(err);\n return reply.status(500).send({ error: 'internal_error', message });\n });\n\n // Auth runs before routing, so an unknown write path cannot leak whether it\n // exists. A missing credential is a 401 with a challenge, never a silent\n // pass and never a 404 pretending the route moved.\n if (auth) {\n app.addHook('onRequest', async (req, reply) => {\n if (!isWrite(req.method)) return;\n try {\n auth.authenticate(req.headers, 'report');\n } catch (err) {\n if (AuthError.is(err)) {\n if (err.status === 401) reply.header('WWW-Authenticate', 'Bearer realm=\"rein-graph\"');\n return reply.status(err.status).send({ error: err.code, message: err.message });\n }\n throw err;\n }\n return;\n });\n }\n\n app.get('/health', () => ({\n status: 'ok',\n subjects: graph.subjects(),\n // Advertised so a producer can tell \"this graph wants a key\" apart from\n // \"my key is wrong\" without guessing from a 401.\n auth: auth ? 'api-key' : 'none',\n }));\n\n // --- Ingestion ---\n // The HTTP path can await durability even though the in-process bus path\n // cannot — flush the durable writes before answering so a POST that returns\n // 200 is persisted.\n app.post('/v1/events', async (req) => {\n const events = z.union([ReinEvent.transform((e) => [e]), z.array(ReinEvent)]).parse(req.body);\n for (const event of events) graph.ingest(event);\n await graph.flush();\n return { ingested: events.length };\n });\n\n app.post('/v1/reports', async (req) => {\n const input = ReportInput.parse(req.body);\n graph.report(input);\n await graph.flush();\n return graph.score(input.subject);\n });\n\n // --- Identity links ---\n // Unverified by DESIGN: this endpoint's trust level equals POST /v1/events —\n // whoever can post events can already fabricate the evidence itself. On-chain\n // verification belongs to the CALLER (@reinconsole/erc8004 derives link facts from\n // the Identity Registry, then asserts them here).\n app.post('/v1/links', async (req) => {\n const links = z.union([LinkInput.transform((l) => [l]), z.array(LinkInput)]).parse(req.body);\n for (const link of links) graph.link(link.canonical, link.alias);\n await graph.flush(); // durable merges land before the 200 (same contract as /v1/events)\n return { linked: links.length };\n });\n\n // --- Scores ---\n app.get('/v1/scores', (req) => {\n const { kind } = ScoresQuery.parse(req.query);\n return graph.scores(kind);\n });\n\n app.get('/v1/scores/:kind/:id', (req, reply) => {\n const subject = ReputationSubject.parse(req.params);\n const explanation = graph.explain(subject);\n if (!explanation) {\n return reply\n .status(404)\n .send({ error: 'not_found', message: `no evidence for ${subject.kind} ${subject.id}` });\n }\n return explanation;\n });\n\n return app;\n}\n\nconst LOOPBACK = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);\n\n/**\n * Build the graph's auth layer from the environment, or explain why there is\n * none. `REIN_GRAPH_API_KEY` seeds one or more comma-separated secrets.\n *\n * They are seeded with `report` and nothing else: the graph has no key-issuing\n * routes to bootstrap (unlike the engine, which seeds `admin` so `/v1/keys` is\n * reachable), so an `admin` secret here would grant authority no route needs.\n */\nexport async function graphAuthFromEnv(env: NodeJS.ProcessEnv): Promise<ApiKeyAuth | undefined> {\n const raw = env['REIN_GRAPH_API_KEY']?.trim();\n if (!raw) return undefined;\n const auth = new ApiKeyAuth();\n const secrets = raw\n .split(',')\n .map((secret) => secret.trim())\n .filter(Boolean);\n for (const [i, secret] of secrets.entries()) {\n await auth.issue({ name: `env-key-${i + 1}`, scopes: ['report'], secret });\n }\n return auth.hasKeys() ? auth : undefined;\n}\n\n/**\n * Where a graph process binds, given whether auth exists.\n *\n * Until D1(a) the graph had no key to trade, so the only trade available was a\n * statement: `REIN_GRAPH_PUBLIC=1`. Now that writes can demand a `report` key,\n * the rule matches the engine's — a key buys a public bind, and asking for one\n * without a key is a startup ERROR rather than a warning. `REIN_GRAPH_PUBLIC=1`\n * survives as the deliberate override for an open graph, because a read-only\n * showcase is a real deployment and reads were never the exposure.\n */\nexport function resolveGraphHost(\n env: NodeJS.ProcessEnv,\n hasAuth = false,\n): { host: string; warning?: string } {\n const requested = env['HOST']?.trim();\n const optedIn = env['REIN_GRAPH_PUBLIC']?.trim() === '1';\n if (hasAuth) return { host: requested || '0.0.0.0' };\n if (optedIn) {\n return {\n host: requested || '0.0.0.0',\n warning:\n 'REIN_GRAPH_PUBLIC=1 — this reputation graph is answering unauthenticated requests. ' +\n 'Anyone who can reach it can write evidence that moves scores.',\n };\n }\n if (requested && !LOOPBACK.has(requested)) {\n throw new Error(\n `refusing to bind ${requested}: the reputation graph has no authentication.` +\n '\\n Set REIN_GRAPH_API_KEY=<secret> to protect its writes, or' +\n '\\n set REIN_GRAPH_PUBLIC=1 to expose an open graph deliberately.',\n );\n }\n return { host: requested || '127.0.0.1' };\n}\n\n// Start the server when run directly (tsx/node), not when imported.\nfunction isMainModule(): boolean {\n if (!process.argv[1]) return false;\n try {\n return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n const port = Number(process.env.PORT ?? 8788);\n // This block used to default to 0.0.0.0 with no auth at all — the in-memory\n // twin of the exposure D1 closed on the durable bin, and the more reachable\n // of the two, since it needs no data directory to run.\n graphAuthFromEnv(process.env)\n .then((auth) => {\n const { host, warning } = resolveGraphHost(process.env, auth !== undefined);\n if (warning) console.warn(`[rein] ${warning}`);\n const app = buildGraphServer(new ReputationGraph(), { ...(auth ? { auth } : {}) });\n return app\n .listen({ port, host })\n .then(() =>\n console.log(\n `[rein] graph listening on http://${host}:${port} (auth: ${auth ? 'api-key' : 'none'})`,\n ),\n );\n })\n .catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n","import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { ApiKeyRecord, toPublicApiKey, type ApiKey, type ApiKeyScope } from './api-key.js';\nimport { newId } from './ulid.js';\n\n/**\n * API-key authentication for a Rein service's HTTP surface.\n *\n * Secrets are never stored: a service keeps sha256 digests, so a leaked\n * database yields nothing that authenticates. A secret is returned exactly\n * once, at issuance. Rotation mints a new secret while keeping the outgoing\n * digest valid for a grace window, so a fleet rolls over without a flag-day\n * restart.\n *\n * This lives in core, beside the {@link ApiKeyRecord} schema it reads and\n * writes, because three services need the SAME answer to \"who is calling and\n * may they do this\" — the engine, the signer's admin surface, and the graph's\n * ingestion routes. Two implementations of authentication is two things to get\n * wrong. It is reached as `@reinconsole/core/auth` rather than through the\n * package barrel ON PURPOSE: the barrel is imported by the console's BROWSER\n * bundle, and every symbol below needs `node:crypto`.\n */\n\n/** A port may answer synchronously (in memory) or not (durable). */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** The presented-secret prefix, so a leaked key is greppable in logs and repos. */\nconst SECRET_PREFIX = 'rk_';\n\n/** How often a key's `lastUsedAt` is written through. Telemetry, not authority. */\nconst TOUCH_INTERVAL_MS = 60_000;\n\n/** Default rotation overlap: long enough to redeploy, short enough to matter. */\nexport const DEFAULT_ROTATION_GRACE_MS = 3_600_000; // 1h\n\nexport function mintSecret(): string {\n return SECRET_PREFIX + randomBytes(32).toString('base64url');\n}\n\nexport function hashSecret(secret: string): string {\n return createHash('sha256').update(secret).digest('hex');\n}\n\n/** Digest comparison that does not leak a prefix match through timing. */\nfunction digestsEqual(a: string, b: string): boolean {\n const left = Buffer.from(a, 'hex');\n const right = Buffer.from(b, 'hex');\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\n/**\n * Constant-time comparison of a presented secret against an expected one.\n *\n * Both sides are hashed first, so the comparison is over two fixed-length\n * digests: `timingSafeEqual` throws on a length mismatch, which would\n * otherwise turn \"wrong length\" into a distinguishable error and leak the\n * secret's size. This is what a service holding ONE static shared secret\n * needs — a key store is the {@link ApiKeyAuth} path above. It lives here so\n * the signer's admin token and the engine's keys agree on what a constant-time\n * secret check is; the signer had its own copy until S52.\n */\nexport function secretsEqual(presented: string, expected: string): boolean {\n return digestsEqual(hashSecret(presented), hashSecret(expected));\n}\n\n/**\n * Persistence seam for keys, mirroring the engine's other ports. Auth is\n * authority state, so writes are persist-then-cache: `put` is awaited before a\n * key counts as issued, rotated, or revoked.\n */\nexport interface ApiKeyStorePort {\n put(record: ApiKeyRecord): MaybePromise<void>;\n get(id: string): ApiKeyRecord | undefined;\n /** Lookup by sha256 of a presented secret (current OR in-grace previous). */\n byHash(hash: string): ApiKeyRecord | undefined;\n list(): ApiKeyRecord[];\n}\n\nexport class InMemoryApiKeyStore implements ApiKeyStorePort {\n private readonly byId = new Map<string, ApiKeyRecord>();\n private readonly hashes = new Map<string, string>(); // secretHash -> keyId\n\n put(record: ApiKeyRecord): void {\n const existing = this.byId.get(record.id);\n if (existing) {\n this.hashes.delete(existing.secretHash);\n if (existing.previousSecretHash) this.hashes.delete(existing.previousSecretHash);\n }\n this.byId.set(record.id, record);\n this.hashes.set(record.secretHash, record.id);\n if (record.previousSecretHash) this.hashes.set(record.previousSecretHash, record.id);\n }\n\n get(id: string): ApiKeyRecord | undefined {\n return this.byId.get(id);\n }\n\n byHash(hash: string): ApiKeyRecord | undefined {\n const id = this.hashes.get(hash);\n return id === undefined ? undefined : this.byId.get(id);\n }\n\n list(): ApiKeyRecord[] {\n return [...this.byId.values()];\n }\n}\n\nexport type AuthFailureCode =\n | 'missing_credentials'\n | 'invalid_key'\n | 'key_revoked'\n /** Past the key's `expiresAt` (a sandbox key nobody claimed). */\n | 'key_expired'\n | 'secret_expired'\n | 'insufficient_scope'\n | 'unknown_key_id'\n /** The key is confined to an org, and the named agent is not in it. */\n | 'agent_not_in_scope'\n /**\n * The key is confined to an org and the route has no tenant rule, so there\n * is no way to confine the call. Fail closed: an unclassified route is\n * reachable only by an unscoped operator key, exactly as an unclassified\n * route already demands `admin`.\n */\n | 'route_not_scopable';\n\n/**\n * Every rejected request throws one of these. It carries the HTTP status so a\n * route never has to guess: 401 means \"we do not know who you are\", 403 means\n * \"we know, and this key may not do that\". Nothing fails open, nothing fails\n * silently.\n */\nexport class AuthError extends Error {\n /**\n * A brand, because `instanceof` is not enough here. Every service bundles\n * `@reinconsole/core` into its own output (`noExternal`, for self-contained\n * deploys), so an `ApiKeyAuth` built from one package and handed to a server\n * in another throws an AuthError from a DIFFERENT copy of this class. An\n * `instanceof` check in the catching service then misses it and a 401 turns\n * into a 500 — which is what happened the first time the graph was handed\n * the engine's auth object.\n */\n readonly reinAuthError = true;\n\n constructor(\n readonly status: 401 | 403 | 404,\n readonly code: AuthFailureCode,\n message: string,\n ) {\n super(message);\n this.name = 'AuthError';\n }\n\n /** Use this, never a bare `instanceof`, when catching across packages. */\n static is(err: unknown): err is AuthError {\n return (\n err instanceof AuthError ||\n (typeof err === 'object' &&\n err !== null &&\n (err as { reinAuthError?: unknown }).reinAuthError === true)\n );\n }\n}\n\nexport interface IssuedApiKey {\n key: ApiKey;\n /** The plaintext secret. Shown once, at issuance; never recoverable after. */\n secret: string;\n}\n\nexport interface ApiKeyAuthOptions {\n store?: ApiKeyStorePort;\n /** Injected clock, so grace-window behaviour is testable without waiting. */\n now?: () => number;\n}\n\nexport class ApiKeyAuth {\n private readonly store: ApiKeyStorePort;\n private readonly now: () => number;\n /**\n * Keys with an authority write (rotate, revoke, lift) in flight. A usage\n * touch writes back a WHOLE record snapshot, so one taken while such a write\n * is on its way to disk would land after it and undo it -- re-open a revoked\n * key, or give a claimed sandbox its expiry back. Touches skip these keys.\n */\n private readonly mutating = new Set<string>();\n\n constructor(options: ApiKeyAuthOptions = {}) {\n this.store = options.store ?? new InMemoryApiKeyStore();\n this.now = options.now ?? Date.now;\n }\n\n /**\n * Mint a key. The secret in the result is the only copy that will exist.\n *\n * `orgId` confines the key to one tenant; omitting it mints the unscoped\n * operator key that every deployment had before tenancy. `agentIds` narrows\n * an org-scoped key to named agents and is refused without an org, because a\n * scope with no org cannot say which policies or approvers the key may\n * reach — and a half-defined scope is the kind that fails open.\n */\n async issue(input: {\n name: string;\n scopes: ApiKeyScope[];\n /** Confine the key to one org. Omit for an unscoped operator key. */\n orgId?: string;\n /** Narrow an org-scoped key to named agents (max 64). */\n agentIds?: string[];\n /** Adopt a caller-supplied secret (env-seeded boot keys). */\n secret?: string;\n /** Stop authenticating at this instant (sandbox keys). Omit for never. */\n expiresAt?: Date;\n }): Promise<IssuedApiKey> {\n if (input.scopes.length === 0) throw new TypeError('an API key needs at least one scope');\n if (input.agentIds?.length && input.orgId === undefined) {\n throw new TypeError('agentIds narrows an org-scoped key; pass orgId as well');\n }\n const secret = input.secret ?? mintSecret();\n const record = ApiKeyRecord.parse({\n id: newId('key'),\n name: input.name,\n scopes: input.scopes,\n ...(input.orgId !== undefined ? { orgId: input.orgId } : {}),\n ...(input.agentIds?.length ? { agentIds: input.agentIds } : {}),\n createdAt: new Date(this.now()),\n ...(input.expiresAt !== undefined ? { expiresAt: input.expiresAt } : {}),\n secretHash: hashSecret(secret),\n });\n await this.store.put(record);\n return { key: toPublicApiKey(record), secret };\n }\n\n /**\n * Mint a replacement secret for an existing key. The outgoing secret keeps\n * working for `graceMs` so callers can be redeployed one at a time; pass 0\n * to cut it off immediately (what a compromised secret needs).\n */\n async rotate(keyId: string, options: { graceMs?: number } = {}): Promise<IssuedApiKey> {\n const record = this.store.get(keyId);\n if (!record) throw new AuthError(404, 'unknown_key_id', `no such key: ${keyId}`);\n const graceMs = options.graceMs ?? DEFAULT_ROTATION_GRACE_MS;\n const secret = mintSecret();\n const rotated: ApiKeyRecord = {\n ...record,\n secretHash: hashSecret(secret),\n rotatedAt: new Date(this.now()),\n };\n delete rotated.previousSecretHash;\n delete rotated.previousSecretExpiresAt;\n if (graceMs > 0) {\n rotated.previousSecretHash = record.secretHash;\n rotated.previousSecretExpiresAt = new Date(this.now() + graceMs);\n }\n await this.write(rotated);\n return { key: toPublicApiKey(rotated), secret };\n }\n\n async revoke(keyId: string): Promise<ApiKey | undefined> {\n const record = this.store.get(keyId);\n if (!record) return undefined;\n const revoked: ApiKeyRecord = { ...record, revokedAt: new Date(this.now()) };\n await this.write(revoked);\n return toPublicApiKey(revoked);\n }\n\n /**\n * Make an expiring key permanent -- what claiming a sandbox is (Sprint 13).\n * Revoked keys stay revoked: lifting an expiry never resurrects anything.\n * Answers undefined for an unknown key, the record unchanged when there was\n * no expiry to lift.\n */\n async clearExpiry(keyId: string): Promise<ApiKey | undefined> {\n const record = this.store.get(keyId);\n if (!record) return undefined;\n if (record.expiresAt === undefined) return toPublicApiKey(record);\n const lifted: ApiKeyRecord = { ...record };\n delete lifted.expiresAt;\n await this.write(lifted);\n return toPublicApiKey(lifted);\n }\n\n list(): ApiKey[] {\n return this.store.list().map(toPublicApiKey);\n }\n\n /** One key's public record, for an ownership check before rotate/revoke. */\n get(keyId: string): ApiKey | undefined {\n const record = this.store.get(keyId);\n return record === undefined ? undefined : toPublicApiKey(record);\n }\n\n /** True once at least one key exists — what \"auth is configured\" means. */\n hasKeys(): boolean {\n return this.store.list().length > 0;\n }\n\n /**\n * Resolve a request's credentials, or throw. `scope` is what the route\n * needs; an `admin` key satisfies every scope, any other key must hold the\n * exact one.\n */\n authenticate(headers: IncomingHeaders, scope: ApiKeyScope): ApiKey {\n const presented = readCredential(headers);\n if (presented === undefined) {\n throw new AuthError(401, 'missing_credentials', 'missing API key (Authorization: Bearer ...)');\n }\n\n const hash = hashSecret(presented);\n const record = this.store.byHash(hash);\n if (!record) throw new AuthError(401, 'invalid_key', 'unknown API key');\n\n if (!digestsEqual(record.secretHash, hash)) {\n // Only the in-grace previous secret can reach here.\n const expiry = record.previousSecretExpiresAt;\n if (\n record.previousSecretHash === undefined ||\n !digestsEqual(record.previousSecretHash, hash) ||\n expiry === undefined ||\n this.now() >= expiry.getTime()\n ) {\n throw new AuthError(401, 'secret_expired', 'rotated API key secret is no longer accepted');\n }\n }\n\n if (record.revokedAt) throw new AuthError(401, 'key_revoked', 'API key has been revoked');\n if (record.expiresAt && this.now() >= record.expiresAt.getTime()) {\n throw new AuthError(401, 'key_expired', 'API key has expired');\n }\n\n if (!record.scopes.includes('admin') && !record.scopes.includes(scope)) {\n throw new AuthError(403, 'insufficient_scope', `API key lacks the \"${scope}\" scope`);\n }\n\n void this.touch(record);\n return toPublicApiKey(record);\n }\n\n /** An authority write that usage touches must not overtake (see `mutating`). */\n private async write(record: ApiKeyRecord): Promise<void> {\n this.mutating.add(record.id);\n try {\n await this.store.put(record);\n } finally {\n this.mutating.delete(record.id);\n }\n }\n\n /** Throttled write-through of `lastUsedAt` — never on the critical path. */\n private async touch(record: ApiKeyRecord): Promise<void> {\n const at = this.now();\n if (this.mutating.has(record.id)) return;\n if (record.lastUsedAt && at - record.lastUsedAt.getTime() < TOUCH_INTERVAL_MS) return;\n try {\n await this.store.put({ ...record, lastUsedAt: new Date(at) });\n } catch {\n // Usage telemetry must never turn a valid request into a failed one.\n }\n }\n}\n\n/** Just enough of a request's headers to read a credential from. */\nexport interface IncomingHeaders {\n authorization?: string | string[] | undefined;\n 'x-api-key'?: string | string[] | undefined;\n}\n\nfunction first(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * `Authorization: Bearer <secret>` is the primary form; `X-Api-Key: <secret>`\n * is accepted too, because a fair share of agent runtimes only send that.\n */\nexport function readCredential(headers: IncomingHeaders): string | undefined {\n const auth = first(headers.authorization)?.trim();\n if (auth) {\n const match = /^Bearer\\s+(.+)$/i.exec(auth);\n if (match?.[1]) return match[1].trim();\n }\n const apiKey = first(headers['x-api-key'])?.trim();\n return apiKey === undefined || apiKey === '' ? undefined : apiKey;\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","randomBytes","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reinconsole/graph",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Rein's reputation graph (Phase 3) — turns guard receipts and gate receipts into explainable reputation scores, and feeds them back into policy (vendorReputationLt) and gate screening.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/bugiiiii11/rein#readme",
@@ -41,15 +41,15 @@
41
41
  "dependencies": {
42
42
  "fastify": "^5.12.1",
43
43
  "zod": "^3.24.1",
44
- "@reinconsole/core": "^0.4.0"
44
+ "@reinconsole/core": "^0.5.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/node": "^22.10.0",
48
48
  "tsup": "^8.3.5",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^2.1.8",
51
- "@reinconsole/gate": "0.4.0",
52
- "@reinconsole/policy-engine": "0.4.0"
51
+ "@reinconsole/gate": "0.5.0",
52
+ "@reinconsole/policy-engine": "0.5.0"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsup",