export type InitiatorClass = 'human' | 'schedule' | 'agent'; export type ActionVerb = 'contract-call' | 'read' | 'trade-spot' | 'transfer-internal' | 'withdraw' | 'policy-edit'; /** Canonical "what moves" document. Every initiator path compiles to this. */ export interface Intent { schema: 'flows-intent-v1'; intentId: string; orgId: string; initiator: { id: string; class: InitiatorClass; role?: string; }; verb: ActionVerb; templateSlug?: string; installId?: string; venueConnectionId?: string; venueId?: string; /** Merged, schema-validated params — overrides are merged BEFORE compilation. */ params: Record; /** null = unpriceable (fails closed under any notional cap). */ notionalUsd: number | null; /** Certified maximum transaction-fee exposure shown to and bound into approvals. */ maxGasExposureUsd?: number; symbol?: string; asset?: string; destination?: string; /** * contract-call only — the bounded call this intent proposes. The gate * checks these against the manifest's contractCall constraint block (a * SEPARATE enforced input — never a permission-widener): * to — the target contract address * selector — the 4-byte function selector (0x-prefixed, e.g. 0xabcdef12) * value — native value in wei (string; '0' or absent = no value moved) * chainId — the chain this call targets; the gate requires it to match the * manifest's contractCall.chainId EXACTLY (a call on the wrong * chain is a different call). A `number` is the coerced chain id; * the string 'INVALID' is the marker compileIntent surfaces when * params.chainId was present but uncoercible — the gate denies it * rather than silently defaulting. * The LIVE constrained-signing seat (Chris-gated) re-derives + re-checks * these from the real unsigned tx; here they are the dev-time candidate. */ to?: string; selector?: string; value?: string; chainId?: number | 'INVALID'; tolerances?: { maxSlippageBps?: number; }; /** * RISK FACTS (slice 2 — risk-aware policy). SERVER-RESOLVED, never read from `params` — * the host resolves them from the curated catalog (protocolRisk.service) for the vault/ * token this move targets and stamps them here, EXACTLY like `screening` is a host- * supplied fact (a caller-settable score would be a trivial bypass of the risk gate). * The gate is pure — it judges these facts, it never derives or fetches them. * protocol — the DefiLlama `project` slug the resolved vault belongs to. * protocolRiskScore — the curated 1–10 smart-contract risk score for that protocol. * protocolRiskUnknown — TRUE when the protocol is NOT in the curated catalog (an * unscorable slug). The gate refuses it under ANY risk policy via * THIS flag, not the numeric ceiling (a maxScore of 10 must NOT * silently allow an unscored protocol) — CODEX-M1 #3. * assetClass — the curated class of the asset moved (stable/bluechip/major/other). * Absent on a move a risk policy governs ⇒ the gate fail-closes (see the risk block). */ protocol?: string; protocolRiskScore?: number; protocolRiskUnknown?: boolean; assetClass?: 'stable' | 'bluechip' | 'major' | 'other'; /** ISO — past this, the intent denies (holds auto-cancel via request expiry). */ expiresAt?: string; reason: string; parentIntentRef?: string; mode: 'dryrun' | 'live'; createdAt: string; } export type ScreeningStatus = 'clear' | 'sanctioned' | 'high_risk' | 'unknown'; /** * Sanctions / AML screening of an intent's external destination. Fetched by * the host BEFORE evaluation (a network call — the evaluator stays pure) and * passed in as a fact. Attested in the receipt alongside the verdict. * clear — not on any sanctions list (and, with KYT, below risk threshold) * sanctioned — on an OFAC/EU/UN list → hard deny, no approval waives it * high_risk — KYT risk above threshold → holds for a human * unknown — screening was unavailable → holds withdrawals (fail-safe) */ export interface AddressScreening { address: string; status: ScreeningStatus; source: string; categories?: string[]; riskScore?: number; screenedAt: string; } export interface CapBlock { maxNotionalPerOrderUsd?: number; maxNotionalPerDayUsd?: number; } /** * The org's NATIVE-GAS allowance — a SEPARATE policy axis from the notional caps * (hermes v8). Without this block, a simulated transaction's maximum native fee * (simulationFacts.gas.maxCostUsd) is ADDED to the principal exposure and judged * against the notional caps — safe, but it makes every low-notional onchain move * impossible under a least-privilege policy (a conservative multi-op gas reserve * is hundreds of times the sub-dollar principal). With at least one cap set here, * the gate judges the two components separately: principal exposure against the * notional caps, maximum native gas against THESE caps. * * ACTIVATING the split (adding this block where none existed) is a policy * LOOSENING — the same simulated tick that denied under the asset caps can now * allow — so it always rides the held/sign-off ceremony (classifyPolicyEdit). * An empty block ({}) is treated as ABSENT (legacy fold-in), mirroring hasAnyCap. * The per-day cap is judged against SpendState.gasDayUsd; a LIVE move under a * set day cap with an unknown tally DENIES (fail closed, the per-grantee * day-cap precedent). */ export interface GasCapBlock { maxUsdPerOrder?: number; maxUsdPerDay?: number; } /** * A named standing-authorization's leash (requirement R2 — "Action = governed * capability"). This is `initiatorClassAllowances` promoted from a per-CLASS * default to a per-NAMED-GRANTEE grant: "member/agent/api-key X may do verb Y on * venues V up to $N/day until E". The HOST does the DB lookup (find the ACTIVE, * non-expired StandingAuthorization for the authenticated initiator) and passes * the matched grant's bounds here as a SEPARATE enforced gate input — the * evaluator stays PURE (no DB, no clock; the grant's bounds + allowlists arrive * as facts). * * Enforced as a MOST-RESTRICTIVE bound (∩ org policy ∩ install bounds), never a * widener: * - caps over the grant's per-order/day → the same DENY class as a class-allowance * breach (a named grantee cannot escalate itself past its leash); * - a verb / venue / symbol NOT on the grant's allowlist → DENY (see the * allowlist convention below); * - a grant-CONSTRAINED axis the intent cannot supply a value for → DENY (fail * closed — a constrained axis the gate can't verify must not pass); * - unpriceable notional under a grant cap → DENY (fail closed). * * ALLOWLIST CONVENTION (GRANTS ONLY — distinct from the org-policy allowlists, * where empty/absent = unconstrained). For a GRANT axis: * - `undefined` ⇒ this grant does not constrain the axis (the org + * manifest still do); * - a DEFINED array ⇒ "the value MUST be in this list". An empty defined * (even empty) array is therefore DENY-ALL on that axis (nothing is * in []). This is what makes two disjoint grants merge * to deny-all (∩ of {X} and {Y} = []), not allow-all. * The host (resolveGranteeAllowance) materializes this: an unconstrained axis is * left undefined; an intersection of two disjoint constrained lists is [] (defined). * * The fail-closed "a standing-auth initiator with NO matching grant denies" * (never falls through to a looser class allowance) is carried by * EvaluateInput.requireGranteeAllowance, NOT here — this type only describes a * grant that WAS found. */ export interface GranteeAllowance { caps: CapBlock; /** Verbs the grant authorizes. `undefined` ⇒ unconstrained; a DEFINED array (even []) ⇒ verb MUST be in it ([] = deny-all). */ verbAllowlist?: ActionVerb[]; /** Venues the grant authorizes (matched against intent.venueId). `undefined` ⇒ unconstrained; DEFINED (even []) ⇒ must be in it ([] = deny-all). */ venueAllowlist?: string[]; /** Symbols the grant authorizes (matched against intent.symbol / params). `undefined` ⇒ unconstrained; DEFINED (even []) ⇒ must be in it ([] = deny-all). */ symbolAllowlist?: string[]; /** * The grantee's USD already spent today (per-GRANTEE, NOT the per-class tally), * supplied by the HOST. The grant's per-day cap is checked as * `(spentTodayUsd ?? 0) + notionalUsd > caps.maxNotionalPerDayUsd` ONLY for the * dry-run preview; in the LIVE path, if `caps.maxNotionalPerDayUsd` is set but * `spentTodayUsd` is undefined (the host could not compute the tally), the * day-cap cannot be assured → DENY (fail closed — do NOT default to 0-spent live). * W5: live host must populate spentTodayUsd from a per-grantee tally across all * live money-event kinds (not just automation.tick). */ spentTodayUsd?: number; } /** * Org law. Stored in Postgres, hash-registered with chipotle (artifact #9). * * SCHEMA VERSIONS (bundle-first migration, plans/bundle-first-policy.md): * - 'flows-policy-v1' — the legacy full-authoring doc. `initiatorClassAllowances` * and `allowedApiHosts` are LIVE law; semantics are byte-identical to * flows-policy@0.2.8. * - 'flows-policy-v2' — the shrunken Layer-1 "account constitution": caps * (the day ceiling) + circuits + compliance + selfWallets + notify. The two * retired fields MUST be absent; under v2 the gate re-homes their semantics * to the owner-approved bundle: * · agent leash → an ACTIVE standing-authorization grant IS the leash, for * BOTH agent- and schedule-class live fund moves (no grant → deny); * · egress allow-side → the approved manifest's hosts * (EgressConstraints.approvedHosts), not an org list. * The doc is hash-verified + rotation-attested in-TEE, so this switch is * per-org and unforgeable — a host cannot flip an org's semantics by lying * about the schema tag. Migrating v1 → v2 is itself a governed ceremony * (classifyPolicyEdit ranks any schema change as a loosening → held). */ export interface PolicyDoc { schema: 'flows-policy-v1' | 'flows-policy-v2'; orgId: string; version: number; caps: CapBlock; /** Separate native-gas allowance axis — see GasCapBlock. Absent/empty ⇒ gas * folds into the notional exposure (the legacy conservative behavior). */ gas?: GasCapBlock; /** Keyed by venueConnectionId — per-wallet/per-account sections. */ perWallet?: Record; /** * Per-initiator-class allowances. An agent with NO allowance entry is * denied live fund movement — agents must be explicitly leashed; * granting or raising an allowance is a loosening. */ initiatorClassAllowances?: Partial>; allowlists?: { symbols?: string[]; venues?: string[]; destinations?: string[]; }; /** * The org's OWN on-chain wallet addresses (the PKP-rooted asset wallet + the * passkey SCA). A `transfer-internal` move that carries an on-chain destination * is only legitimate when that destination is one of these — any other * destination IS a withdrawal wearing the wrong verb (see the * `transfer-internal.destinationNotOwn` deny). Populated by the TRUSTED host * from the derived wallet addresses; the policy hash is verified in-TEE, so an * agent cannot forge it. Compared case-insensitively. Fail-closed: unset/empty * means a destination-bearing transfer-internal has no own-wallet to match and * is denied (you must declare your wallets to transfer to them). */ selfWallets?: string[]; /** * The off-chain API hosts this org permits a deployed action to reach (egress). * SET (non-empty) ⇒ an action may declare only these hosts; any declared host * outside the set is denied (egress.hostNotAllowed). UNSET/empty ⇒ egress is not * permitted at all — any action that declares egress is denied (egress.notPermitted, * fail-closed). Adding a host is a policy LOOSENING (see classifyPolicyEdit). This * is a DECLARATION gate only — the TEE-enforced runtime egress is a separate seam. */ allowedApiHosts?: string[]; circuits: { /** Withdrawals are never autonomous — the sweep ceremony lives HERE now. */ withdrawalsAlwaysHold: boolean; holdAssurance: 'L1' | 'L2' | 'L3'; holdTtlSec: number; /** Team orgs hold loosenings for a second human; solo presets time-lock. */ policyLoosening: 'hold' | 'timelock'; timelockSec?: number; /** * WHO may release a hold / finalize a held loosening. Maker-checker is across * CREDENTIALS, not identities: the initiating credential (session, agent key) * never releases its own hold; this picks the independent checker. * 'self' — the initiating human (or the owning human of an initiating * agent key) releases with the request's assurance step-up * (the account passkey at L2 — the solo-org mode). * 'second-person' — a DIFFERENT human with APPROVER/ADMIN role (guardian/team). * 'quorum' — M-of-N SCA owner sign-offs (multi-owner governed accounts). * ABSENT ⇒ derived from the root account config at read time: multi-owner SCA * orgs get 'quorum', everyone else 'self'. The server enforces this (the release * ceremony is not in the TEE trust path); classifyPolicyEdit ranks it. */ approval?: 'self' | 'second-person' | 'quorum'; }; notify?: { onLiveRunOverUsd?: number; }; /** * AML/sanctions screening of external destinations (Chainalysis). Absent = * the conservative default (screen withdrawals, hold when screening is down). * Turning either off is a policy LOOSENING. */ compliance?: { screenWithdrawals: boolean; holdOnUnknown: boolean; }; /** * RISK-AWARE policy (slice 2 — the differentiator: "max smart-contract risk 6/10, * stables + blue-chip only"). The LAW only — caps + the allowed set, never any running * tally (the per-protocol exposure STATE lives in SpendState.perProtocolDayUsd, CODEX-M1 * #1: putting state in the doc would make it stale/editable and break classifyPolicyEdit). * Absent ⇒ no risk dimension is enforced. The gate judges these against the SERVER- * RESOLVED risk facts on the intent (Intent.protocolRiskScore / assetClass / etc.). * maxProtocolRiskScore — ceiling on the curated 1–10 smart-contract risk score; a * resolved protocol OVER it is a structural DENY (and an * UNSCORABLE protocol DENIES regardless — see the gate). * allowedAssetClasses — the asset classes a move may touch; a class outside the * set is a structural DENY ("stables + blue-chip only"). * maxExposurePerProtocolUsd— per-protocol exposure cap; a move that would push the * protocol's running exposure (SpendState.perProtocolDayUsd) * over it HOLDs (cap-class, waivable). Absent tally under a * cap fail-closes to a HOLD. */ risk?: { maxProtocolRiskScore?: number; allowedAssetClasses?: ('stable' | 'bluechip' | 'major' | 'other')[]; maxExposurePerProtocolUsd?: number; }; } /** Today's consumed live notional, assembled by the host, attested in receipts. */ export interface SpendState { schema: 'flows-spend-state-v1'; /** UTC day bucket, YYYY-MM-DD. */ date: string; orgDayUsd: number; perWalletDayUsd?: Record; perInstallDayUsd?: Record; perInitiatorClassDayUsd?: Partial>; /** * Per-protocol USD exposure already taken today, keyed by DefiLlama `project` slug — * the DYNAMIC STATE the risk block's maxExposurePerProtocolUsd cap is checked against * (CODEX-M1 #1: the running tally lives HERE, not in PolicyDoc). The host assembles it * the same way it assembles orgDayUsd. A PREVIEW passes 0/absent → fail-closed HOLD * under a cap (mirrors the per-grantee day-cap precedent), so a dry-run never under- * counts exposure into an allow. */ perProtocolDayUsd?: Record; /** * Maximum-native-gas USD already reserved by today's live onchain dispatches — * the running tally the gas axis's maxUsdPerDay (PolicyDoc.gas) is judged * against when the gas/notional split is active. Assembled by the host from * the same audit log as orgDayUsd (payload.gasUsd on live dispatch events). * UNDEFINED under a set day cap: dry-run previews at 0; LIVE DENIES (fail * closed — mirrors the per-grantee day-cap rule). */ gasDayUsd?: number; } /** * What a template may request — the app-store permission sheet for money. * Intersection semantics: effective permission = manifest ∩ policy; a verb * not declared here is denied regardless of policy. */ export interface CapabilityManifest { schema: 'flows-capability-manifest-v1'; verbs: ActionVerb[]; } /** * The bounded-call constraint for a `contract-call` action. This is a SEPARATE * enforced input to the gate — NOT part of the CapabilityManifest the safety * reducer produces — so it can only NARROW what a contract-call may do, never * widen it. Mirrors validateUnsignedTx's {to-allowlist, selector, value-cap} * guard (defi/execution.ts) for the dev-time dry-run; the LIVE seat enforces the * same shape on the real unsigned tx (Chris-gated). Fail-closed: an empty * toAllowlist or selectorAllowlist means NOTHING is callable (a deny), never * "anything goes". maxValueWei absent = no native value may move (cap is 0). */ export interface ContractCallConstraints { /** Contracts a call may target (checksummed or lowercase — compared case-insensitively). */ toAllowlist: string[]; /** 4-byte selectors a call may invoke, 0x-prefixed (compared case-insensitively). */ selectorAllowlist: string[]; /** Max native value in wei (decimal string). Absent ⇒ '0' ⇒ no native value may move. */ maxValueWei?: string; /** The chain this constraint is scoped to (carried for the live seat; informational here). */ chainId: number; } /** * The egress (off-chain API) declaration for a deployed action — the API hosts it * intends to reach (e.g. a yields aggregator, a 0x/1inch route API, a CEX REST). * A SEPARATE enforced input to the gate — NOT part of the CapabilityManifest the * safety reducer produces — so it can only NARROW (gate) what an action may reach, * never widen any fund verb. Egress is NOT a verb: an action with verb `trade-spot` * may ALSO declare egress to fetch a route. Fail-closed: an empty apiHosts is * rejected at parse; egress with no `policy.allowedApiHosts` permitting it is denied. * DECLARATION ONLY — the TEE-enforced runtime egress allowlist is a separate * Chris-gated seam (this gate does not perform any fetch). */ export interface EgressConstraints { /** Hostnames the action declares it will call (lowercased; compared case-insensitively). */ apiHosts: string[]; /** * BUNDLE-FIRST (flows-policy-v2 orgs only; ignored under v1): the hosts the * OWNERS approved when they approved this bundle — i.e. the approved manifest's * declared egress hosts, shown on the Grants-power approval card and bound to * the approval by CID + manifestHash pinning. Under v2 the allow-side check is * declared ⊆ approvedHosts (org `allowedApiHosts` is retired and never * consulted); absent/empty under v2 is a fail-closed deny — an unapproved * bundle reaches nothing. TRUST: a seat must populate this ONLY from a * VERIFIED source (the attested grant-status body's manifestEgressHosts / * an attested manifest), NEVER from bare host-supplied jsParams — otherwise a * compromised host could approve its own egress. SSRF-class hosts still deny * unconditionally regardless of approval. */ approvedHosts?: string[]; } /** Per-install sub-budget (legacy bounds become sub-allocations of org law). */ export interface InstallBounds { maxNotionalPerOrderUsd?: number; maxNotionalPerDayUsd?: number; symbolAllowlist?: string[]; venueAllowlist?: string[]; assuranceRequired?: 'L1' | 'L2' | 'L3'; } export type VerdictKind = 'allow' | 'allow_with_notify' | 'hold' | 'deny'; export interface VerdictTrace { /** Machine rule id, e.g. "org.maxNotionalPerDayUsd" | "manifest.verb". */ rule: string; /** Human sentence — renders on the deny card / approval card verbatim. */ detail: string; consumedUsd?: number; remainingUsd?: number; /** What would change the outcome ("reduce to ≤ $1,600, or request approval"). */ changePath?: string; } export interface Verdict { schema: 'flows-verdict-v1'; kind: VerdictKind; traces: VerdictTrace[]; hold?: { assurance: 'L1' | 'L2' | 'L3'; ttlSec: number; reason: 'bounds' | 'circuit' | 'tolerance' | 'policy-edit' | 'compliance'; }; policyVersion: number; policyHash: string; evaluatedAt: string; } /** Authenticated, executor-produced facts for the exact prepared operation. The * enforcing seat signs these in its receipt; callers cannot supply them as a * policy widener. Policy deliberately consumes only the normalized shape. */ export interface SimulationFacts { schema: 'flows-simulation-facts-v1'; coverage: 'exact' | 'partial' | 'unavailable'; operationHash: string; chainId?: number; success: boolean; revert?: { code: string; reason?: string; operationIndex?: number; }; assetChanges: Array<{ amountRaw: string; direction: 'in' | 'out'; usd?: number; }>; permissionChanges: Array<{ kind: 'erc20-allowance' | 'erc721-approval' | 'operator' | 'other'; asset?: string; grantee?: string; after?: string; tokenId?: string; usdExposure?: number; }>; debtChanges?: Array<{ usdIncrease?: number; }>; executorConservativeBasisUsd?: number; /** nativeUsd/nativeUsdSource: the executor's attested USD basis for the native * asset ('anchor-ref-pool' = in-TEE CID-pinned WETH/USD reference pool; * 'conservative-ceiling' = the fail-closed $100k/ETH constant). Informational — * the gate judges maxCostUsd; the basis is for receipts + diagnosis. */ gas?: { estimated: string; maxCostNative: string; maxCostUsd: number; nativeUsd?: number; nativeUsdSource?: string; }; warnings?: string[]; } export interface EvaluateInput { intent: Intent; policy: PolicyDoc; /** sha256(JCS(policy)) — the host verifies it against the chipotle registry. */ policyHash: string; spend: SpendState; manifest: CapabilityManifest; /** * The bounded-call constraint for a `contract-call` intent — a SEPARATE * enforced input (never folded into the manifest). REQUIRED for a * `contract-call` intent: its absence is a hard deny (an unconstrained * arbitrary call must never be permitted). Ignored for every other verb. */ contractCall?: ContractCallConstraints; /** * The egress (off-chain API) declaration for the action — a SEPARATE enforced * input (never folded into the manifest). When present, the gate hard-denies if * any declared host is SSRF-class (egress.ssrfHost), if `policy.allowedApiHosts` * is set and a host is outside it (egress.hostNotAllowed), or if the policy does * not permit egress at all (egress.notPermitted). Absent ⇒ no egress checks run * (an action that declares no egress is unaffected). DECLARATION gate only. */ egress?: EgressConstraints; installBounds?: InstallBounds | null; /** * The matched standing-authorization's leash (requirement R2). Set by the HOST * when it found an ACTIVE, non-expired StandingAuthorization for this intent's * authenticated initiator (the lookup is host-side; the bounds arrive here as a * fact so the gate stays pure). When present it is enforced as a MOST-RESTRICTIVE * bound (∩ org ∩ install): caps over the grant DENY (the grantee cannot escalate * past its leash), and a verb/venue/symbol outside the grant's allowlist DENIES. * See GranteeAllowance. Independent of `approvalGranted` — a per-move approval * never widens a standing grant's leash. */ granteeAllowance?: GranteeAllowance; /** * FAIL-CLOSED marker (requirement R2). True ⇒ this intent is governed by the * standing-authorization regime (the host classified its authenticated initiator * as a named grantee that must act under a grant) and therefore REQUIRES a * matching `granteeAllowance`. If true and `granteeAllowance` is absent (no * ACTIVE / non-expired / non-revoked grant matched), a live fund-moving intent * DENIES — it must NOT fall through to a more permissive `initiatorClassAllowances` * class default. This is what makes "no/expired/revoked grant → deny" hold: the * host sets it for any initiator it routes through the standing-auth path, and * only attaches `granteeAllowance` when a real grant was found. */ requireGranteeAllowance?: boolean; /** * True ONLY after the host verified a single-use approval attestation whose * boundHash matches this intent's hash. Waives hold-class rules (that is * what an approval authorizes); never waives denies. */ approvalGranted?: boolean; /** * Host-computed current notional for tolerance re-validation on approved * re-entry. null/undefined = no fresh price available (re-validation skipped * only when the intent declares no tolerance). */ currentNotionalUsd?: number | null; /** Exact-operation simulation facts produced inside the enforcing seat. */ simulationFacts?: SimulationFacts; /** * Sanctions/AML screening of intent.destination, fetched by the host before * the call. Absent on a withdrawal-with-destination is treated as 'unknown' * (fail-safe hold), so the gate is never less safe for a missing screen. */ screening?: AddressScreening; /** * TRUSTED-HOST marker: this `transfer-internal` destination is a certified DeFi * vault deposit (the host RESOLVED `intent.destination` from its HAND-VETTED * on-chain vault registry — yieldResolve.service / venueCatalog). It does two * things, both fail-safe: * 1. Waives the `transfer-internal.destinationNotOwn` deny for THIS destination — * a vetted vault is a legitimate deposit target (shares mint to your own PKP), * not a withdrawal-in-disguise to an arbitrary address. The host vouches the * address came from the vetted registry; an UNvetted transfer-internal with a * destination still denies (the host never sets this without a registry hit). * 2. Turns ON destination screening for the deposit (the silent-allow fix): with * this marker the gate HOLDs a deposit whose screen is non-clear/absent/unknown. * This screen is UNCONDITIONAL — it runs regardless of compliance.screenWithdrawals * and compliance.holdOnUnknown (CODEX-HIGH #1). Those toggles are a user's choice * for WITHDRAWALS; the deposit-screen is the non-optional safety that REPLACES the * waived destinationNotOwn deny, so it must never be a no-op. Sanctioned still * DENIES (hard, far above); high-risk still HOLDS. Only `clear` (and matching the * destination) releases it. The replacement for `destinationNotOwn`'s * arbitrary-address guard is therefore "the vault must screen clear" — strictly * safer than the old destination-less deposit (which carried no screen at all). * Absent/false ⇒ every existing transfer-internal path is byte-for-byte unchanged * (a destination-bearing one denies on destinationNotOwn; a destination-less one is * untouched). The evaluator stays pure — it cannot read the registry, so it relies on * this host fact AND enforces clear-screening on top (the host's job is narrow: * resolve a vetted address + set it as the destination + supply its screen). */ vaultDeposit?: boolean; /** * The intent's destination is the caller's OWN deposit address at a connected * venue (e.g. their Coinbase deposit address), RESOLVED + ATTESTED IN-TEE by the * pinned resolver action from the owner's own sealed venue credentials — never a * caller param. Set by the SEAT only after verifying that attestation against its * baked-in resolver CID (a compromised host can relay the signed blob but cannot * substitute the address), so funds can only ever land in the caller's own venue * account. Semantically that is an INTERNAL transfer, not a withdrawal: * 1. Waives the `transfer-internal.destinationNotOwn` deny for THIS destination — * the attested own-venue deposit address is a legitimate self-destination even * though it is not a listed selfWallet (venues mint fresh addresses). * 2. The deposit rides the SAME unconditional deposit-screen as a vaultDeposit: * the screen is the non-optional safety that replaces the waived deny — the * deposit HOLDs unless the destination screens `clear` (sanctioned still hard- * denies far above), regardless of the org's withdrawal-screening toggles. * Absent/false ⇒ every existing transfer-internal path is byte-for-byte unchanged. * The evaluator stays pure — it cannot verify signatures; the seat's verification * (fail-closed, CID-pinned) is what earns this flag. */ verifiedVenueDeposit?: boolean; /** ISO timestamp from the host — the evaluator never reads a clock. */ now: string; } /** * SSRF-class host check (fail-closed). TRUE ⇒ the host must be DENIED: it targets * the host itself, a private/internal network, or a cloud metadata endpoint, OR it * is not a syntactically-valid hostname at all. Covers: localhost; 127.0.0.0/8 * loopback; 0.0.0.0; private ranges 10./192.168./172.16–31.; link-local 169.254. * (incl. the 169.254.169.254 metadata IP); the .internal / .local suffixes. Returns * the matched reason for the trace, or null when the host is a safe public hostname. */ export declare function ssrfReason(hostRaw: string): string | null; /** The gas/notional split is ACTIVE only when the gas block sets at least one * cap — an empty {} is treated as absent (legacy fold-in), never as "split with * no bound" (that would leave the gas axis unbounded). Exported so the host's * reservation + spend accounting mirror the gate's axis choice exactly. */ export declare function hasActiveGasSplit(g: GasCapBlock | undefined): boolean; export declare function evaluate(input: EvaluateInput): Verdict; export type EditClass = 'tighten' | 'loosen'; /** * Classify a policy edit. Loosening if ANY of: a set cap raised or removed, * an allowlist entry added or a list removed, a circuit weakened, a notify * threshold raised or removed, an initiator-class allowance granted or raised. */ export declare function classifyPolicyEdit(before: PolicyDoc, after: PolicyDoc): EditClass;