The human registry for AgentKeys' on-chain contracts: design/version notes, ABI summaries, cutover history, deployer-wallet custody. It deliberately carries no address table — addresses have a single machine source of truth, and re-listing them here is how they drift (this doc once claimed a stale AgentKeysScope address a full redeploy after heima.json had the live one).
| Record | Single source of truth | Notes |
|---|---|---|
| Prod contract addresses + deployed set version | chain profile crates/agentkeys-core/chain-profiles/heima.json — .contracts[] + contract_set_version |
versioned + compiled in (include_str!) — broker/daemon/UI serve it; rewritten by the chain bring-up entry point (operator-internal) on every deploy. Mirrored to scripts/operator-workstation.env (the operator-internal shell mirror); bash scripts/utils/check-deployed-contracts-sync.sh verifies the mirror. |
| Test contract addresses (parallel set) | scripts/operator-workstation.test.env (*_ADDRESS_HEIMA, operator-internal) — authoritative |
the TEST_* GitHub secrets are a CI-consumable copy, synced one-way env→secrets by the CI-secrets sync helper (operator-internal; re-run it after any test redeploy). Never in the chain profile — it records the prod set only. |
| Wallet (EOA) addresses | the env files (*_DEPLOYER_ADDR_HEIMA, BROKER_SPONSOR_SIGNER_ADDRESS_HEIMA) |
key custody tiers + funding map: the chain-setup operator runbook §Wallets (operator-internal) |
| Human prose | this doc | ABI/cutover/version notes ONLY — no addresses |
No doc may re-write a literal address that one of those sources owns — link to the source and give a resolve command instead. CI-enforced: the doc-literal gate in scripts/utils/check-deployed-contracts-sync.sh (workflow contracts-sync.yml) fails any tracked .md containing an address currently in a chain profile. Historical/orphaned addresses pass naturally — once a redeploy moves an address out of the profile, its literal is no longer banned.
Indexed from arch.md §5. (docs/contracts.md is a redirect to this file.) The operator-facing wallet/funding map — key custody tiers, prod-vs-test sets side by side, the funding-flow diagram, "which wallet do I fund" — is the chain-setup operator runbook §Wallets (operator-internal); update it in the same commit as any redeploy/rotation recorded here.
Two distinct EVM accounts deploy AgentKeys contracts. They are different keys, so each lands the contract set at different addresses via (deployer, nonce) CREATE derivation — the prod set and the test set never collide.
| Role | Deployer EVM address (resolve — the env file is the SoT) | Key location |
|---|---|---|
| Local / prod deploy | grep ^DEPLOYER_ADDR_HEIMA scripts/operator-workstation.env |
$HEIMA_DEPLOYER_KEY_FILE (default ~/.agentkeys/heima-deployer.key, never committed) |
| Test / CI deploy | grep ^DEPLOYER_ADDR_HEIMA scripts/operator-workstation.test.env |
~/.agentkeys/heima-deployer-test.key (operator-provided; wired into the TEST_HEIMA_DEPLOYER_KEY GitHub secret via the CI-secrets sync helper (operator-internal)) |
- The prod deployer's Substrate twin (SS58 prefix 31) is how the EVM side gets funded — derive it from the EVM address via the EVM-to-Substrate address helper (operator-internal).
- Heima Paseo testnet uses its own deployer (
HEIMA_PASEO_DEPLOYER_ADDRinoperator-workstation.env) — currently unused (chain halted, see below).
0.5 SOURCE-READY, NOT YET DEPLOYED (#427, agent-slot allowance + on-chain device/delegate kind split).
crates/agentkeys-chain/VERSIONis0.5while the live profile still records0.4— the deliberate VERSION-mismatch hard-stop; the nextheima-bring-up.shrefuses auto-redeploy and requires the explicitFORCE_DEPLOY=1opt-in.SidecarRegistryv0.5 ABI changes: constructor gainsuint16 defaultAllowance(deploy script readsAGENTKEYS_AGENT_SLOT_DEFAULT, default 3);TIER_DEVICE=3added andregisterAgentDevicenow writes it (channel-endpoint DEVICE leg, no slot); new slot-consumingregisterDelegateentrypoint (TIER_AGENT, revertsAgentSlotAllowanceExhaustedwhen the operator's quota is full);revokeAgentDeviceaccepts both K10 tiers and frees a delegate's slot; new owner-onlyset/clearAgentSlotAllowance+setDefaultAgentSlotAllowanceand theagentSlots(bytes32) → (used, total)view;resetMasteralso zeroes the slot state. Like every registry redeploy this ORPHANS the prior set's state (re-run binding ceremonies; commitheima.json+operator-workstation.envin the same change; rebuild the broker from the committed profile — the #225 split-registry rule below), and the fresh K11Verifier forces aP256AccountFactoryre-mint (its constructor pins(entryPoint, k11Verifier); new factory ⇒ new CREATE2 master-account addresses — the 0.4 precedent).heima-deploy-erc4337.shnow detects stale factory wiring and re-mints automatically (verified live 2026-07-12: the first 0.5 run preserved the 0.4-wired factory and setup-heima step 8 verify 5/5 caught it); EntryPoint + VerifyingPaymaster stay preserved (K11-independent). The harness (suite-1 step 12 +heima-agent-create.sh) hard-stops with a redeploy pointer while a pre-0.5 registry is live.
v2 set — contract_set_version 0.4 (current live — prod deployer; router-wired K11 #170 + account-auth #164/#225 + owner-gated resetMaster)
0.4 deployed 2026-06-12 (FORCE full-set redeploy, operator-approved with NO backward compatibility): the K11 chain is now router-wired —
K11Verifier → P256Router → RIP-7212 precompile at0x100(runtime 9261), pure-SolidityP256Verifieras fallback. New core-4 + verifier trio +P256AccountFactory(re-minted against the new K11); EntryPoint + VerifyingPaymaster PRESERVED (K11-independent — deposits intact). On-chain verify cost: 706,696 → 28,372 gas (live-proven same day). The 0.3 set + ALL its on-chain state (master registrations, scopes, epochs, audit rows) is ORPHANED — every operator re-runs the binding ceremonies, and the broker must rebuild from the committed profile (setup-broker-host.sh --ref main).
0.3 deployed 2026-06-09 (FORCE_DEPLOY full-set redeploy — fresh
SidecarRegistry0xC63E6f64…+AgentKeysScope/K3EpochCounter/CredentialAudit, replacing the orphaned 0.20xF50ef960…set). Adds the owner-gatedSidecarRegistry.resetMaster(bytes32 operatorOmni)recovery escape hatch (+ theownerimmutable, captured at construction = the deployer).registerFirstMasterDeviceis first-master-ONLY, so withoutresetMasteran operator who loses/deletes the master passkey could only recover by redeploying the whole set;resetMasterlets the deployer unbind one operator in place.crates/agentkeys-chain/VERSION== the profile'scontract_set_version==0.3(in sync). After ANY such redeploy the broker MUST be rebuilt from the SAME committed profile —setup-broker-host.sh --ref main— or the broker readsoperatorMasterWalletfrom the orphaned old registry while the daemon onboards into the new one (the #225 split-registry accept failure: handleOps revertsSIG_VALIDATION_FAILEDbecause the broker built the UserOp for the old master account). Commitheima.json+operator-workstation.envin the SAME change as the deploy so the broker host can never lag.
RIP-7212 note (2026-06-12): Heima runtime 9261 activated the P-256 precompile at
0x100(litentry/heima#4030, spec-vector-verified on mainnet) and the same-day 0.4 redeploy consumed it (router-wired K11, see above). #170 is resolved on both chains.
Source of truth = the chain profile
crates/agentkeys-core/chain-profiles/heima.json. Itscontracts[]array holds the live addresses;contract_set_versionholds the deployed SET version.scripts/operator/chain/heima-bring-up.shrewrites it programmatically on every fresh deploy (step 6b), and the typedChainProfilestruct +chain_profile::tests::heima_carries_full_contract_registry_and_versionenforce its shape — that is the strict-typed JSON registry. The expected source version lives in../../crates/agentkeys-chain/VERSION; a deploy redeploys + bumps the profile only when the two differ (no bytecode comparison — see "Re-deploy / replace"). This.mdis human PROSE only — it no longer carries an address table (that duplication was the drift source).
Read the live addresses from the profile (don't hand-maintain them here):
jq -r '"contract_set_version \(.contract_set_version)", (.contracts[] | " \(.name): \(.address)")' \
crates/agentkeys-core/chain-profiles/heima.json
# Verify the profile ⟷ operator-workstation.env mirror:
bash scripts/utils/check-deployed-contracts-sync.shThe set: the 4 stage-1 cores AgentKeysScope / SidecarRegistry / K3EpochCounter / CredentialAudit (account-auth #164 E3 — redeployed 2026-06-08, replacing the pre-#164 0xd44b375… / 0x1Ac62f1C… / 0x6c9e675c… / 0x63c4545a… set, now orphaned), the pre-deployed P256Verifier / K11Verifier, the #164 ERC-4337 infra EntryPoint / P256AccountFactory, and the VerifyingPaymaster (#225 — broker-co-signed gas sponsorship for the K11-gated accept UserOp; one shared EntryPoint deposit, the J1 Sybil gate). The parent-control web UI reads the same profile via GET /v1/chain/info (#153).
✅
AgentKeysScope+SidecarRegistryare the #164 account-auth design — cutover landed 2026-06-08. The liveAgentKeysScope(address in the chain profile — the earlier revision of this line carried a stale literal, the exact drift #251 bans) is thesrc/AgentKeysScope.solERC-4337 rewrite:setScope(...)(sel0xd8e9e3c6, no inline K11 tuple) /revokeScope(bytes32,bytes32)(sel0xdcff8c5b), with master writes gated bymsg.sender == operatorMasterWallet(the operator'sP256Account) — biometric authorization moved upstream to the 4337 account'svalidateUserOp. Source now matches the deployed bytecode (the earlier intentionalsrc/≠ deployed divergence is resolved). The pre-cutover stage-1 design (setScopeWithWebauthn(...,K11Assertion), sel0x864ae93c;revokeScope(...,K11Assertion), sel0x6f37dd80) is now orphaned at the old address0xd44b375…(no production state — dev-only); its source is retained atcrates/agentkeys-chain/archived/AgentKeysScope.deployed-stage1.sol(kept per the repo's "move stale to archived, don't delete" policy) so the orphaned bytecode still has findable source. The audit decoder's livescope.grantmapping (audit_decode.rs::onchain_fn) is nowsetScope;calldata::REGISTRYkeeps thesetScopeWithWebauthnFnDef only so the decoder can still resolve orphaned pre-cutover calldata.
The P-256 smart-account master plumbing (plan (operator-internal)), all in the chain profile's .contracts[]:
EntryPoint(ERC-4337 v0.7, deployed 2026-06-02) — canonical eth-infinitism v0.7 bytecode; landed a UserOp end-to-end in the spike.P256AccountFactory— CREATE2 factory;constructor(entryPoint, k11Verifier), wired to the liveK11Verifier; mainnet CREATE2 determinism smoke-verified.VerifyingPaymaster(#225) — broker-co-signed gas sponsorship for the K11-gated accept UserOp; one shared EntryPoint deposit (the J1 Sybil gate). Deployed + funded via the paymaster deploy helper (operator-internal), which rewrites the chain profile + env mirror. Fund the deposit viadeposit(), never a plain transfer — see the chain-setup operator runbook (operator-internal).
Master mutations are UserOps from the operator's P256Account, relayed by the broker to the in-house agentkeys-bundler (#230) — the pre-cutover "inert until masters are registered as accounts" status and the pre-#225 "paymaster intentionally not deployed" note are both superseded.
The test stack deploys the same four contracts with the test deployer key (0x051e…475e), landing them at different addresses (distinct (deployer, nonce) derivation), plus its own ERC-4337 set since #250 — a separate EntryPoint v0.7 + P256AccountFactory (deployed by the ERC-4337 deploy helper (operator-internal), invoked from the chain bring-up entry point's --ci step 6; the test EC2 also runs its own agentkeys-bundler). Full per-env isolation: a test-stack compromise or mis-pointed bundler can never touch prod's EntryPoint deposits/nonces. It shares the prod AWS account but uses distinct IAM roles, S3 buckets, OIDC issuer, and -test DNS — a leaked test cred cannot reach prod data. The test addresses are recorded ONLY in scripts/operator-workstation.test.env (operator-internal; authoritative) and mirrored into the TEST_* GitHub secrets (the CI-consumable copy — synced one-way env→secrets by the CI-secrets sync helper (operator-internal); re-run it after any test redeploy or the workflow runs on stale addresses) — never in the chain profile (it records the prod set; the chain bring-up enforces this).
-
Tier-1 CI (the no-LLM gate from #66/#98) runs against an ephemeral anvil chain — fresh contracts per run, no persistent mainnet addresses.
-
Tier-2 / persistent test deploy addresses are pinned in
scripts/operator-workstation.test.env(*_ADDRESS_HEIMA, operator-internal) — real since the #250 test-set deploy (core set + the test stack's own EntryPoint/factory). Re-pin after a test redeploy, then re-run the CI-secrets sync helper:# --ci selects operator-workstation.test.env AND auto-defaults the deployer # key to ~/.agentkeys/heima-deployer-test.key. Add FORCE_DEPLOY=1 when # refreshing over a live-but-outdated test set. AGENTKEYS_CHAIN=heima MAINNET_CONFIRM=1 \ bash scripts/operator/setup-heima.sh --ci --from-step 4 --to-step 8
-
The
P256Verifier+K11Verifierare shared pre-deployed contracts — same address on prod and test (.test.envmirrors the chain-profile values).
| Contract | Address | Bytecode |
|---|---|---|
AgentKeysScope |
0x14C23B5D1cE20c094af643a20e6b0972dAD12aa8 |
3146 bytes |
SidecarRegistry |
0x76D574a107727bE87fc1422661A030FEFda70786 |
3301 bytes |
K3EpochCounter |
0x8396dEc50ff755d6DE7728DABB00Be2eFBCdf4dF |
687 bytes |
CredentialAudit |
0x1801ded1a4FBD8c9224Ab18B9EcbB293B8674c06 |
1421 bytes |
Halted (block 2,905,430 frozen since 2026-01-15). No contracts deployed — the *_ADDRESS_HEIMA_PASEO entries in operator-workstation.env are placeholders (0x..01–0x..04). When collators return: AGENTKEYS_CHAIN=heima-paseo bash e2e/suite-1-foundation.sh --only-step 9 deploys + auto-funds via Alice sudo; update this doc with the live testnet addresses then.
Source of truth = the chain profile
crates/agentkeys-core/chain-profiles/base.json(.contracts[]+contract_set_version), mirrored toscripts/operator-workstation.base.env(*_BASEkeys, operator-internal). Resolve addresses the same way as heima —jq -r '.contracts[] | "\(.name): \(.address)"' crates/agentkeys-core/chain-profiles/base.json— never paste literals (#251 gate).
Design notes (what differs from the heima 0.3 set):
P256Router— the #170 deliverable. A precompile-first P-256 verifier (RIP-7212P256VERIFYat0x…0100, live on Base since Fjord; flat 3,450 gas) with the pure-SolidityP256Verifierembedded as fallback, wired asK11Verifier'sp256AddrbyDeployAgentKeysV1.s.sol. Every WebAuthn verify (registry mutations,P256AccountUserOp validation) routes through it. The heima 0.3 set predates the router (its K11Verifier points at the bareP256Verifier). Heima's runtime-9261 precompile ACTIVATED 2026-06-12 (litentry/heima#4030; spec-vector-verified on mainnet) — so heima now needs only the 0.4 full-set redeploy to wire the router and inherit the cheap path; the router itself needs no chain-specific variant.EntryPoint= the canonical eth-infinitism v0.7 deployment, ADOPTED not self-deployed (D2 inbase-migration.md(operator-internal)): audited bytes + public-bundler interop;heima-deploy-erc4337.shcode-verifies it before pinning. Per-env isolation holds because prod-heima / prod-base are different chains.- No ED buffer — Base has no Substrate ExistentialDeposit; the AA91 reaping class can't occur (the erc4337 helper skips the buffer on non-substrate chains).
- Version note: this set was deployed under the
0.3label (router as an aux addition) and relabeled0.4the same day when heima's 0.4 router-wired redeploy landed — the deployed bytes on base ARE the 0.4 definition, so both chains +crates/agentkeys-chain/VERSIONnow agree on0.4(no base redeploy was needed, label only).
Deployer: the Base prod deployer — grep ^DEPLOYER_ADDR_BASE scripts/operator-workstation.base.env (key ~/.agentkeys/deployer-base.key, 0600). Gas is ETH. Heima stays live as the consumer free tier (D5 dual-stack) — nothing on this chain replaces it.
Live-proven #170 numbers (2026-06-12): P256Router.verify with a valid P-256 signature = 31,776 gas total (precompile path) vs 683,901 gas through the pure-Solidity verifier on the same chain; invalid signatures correctly return false via the fallback.
First-run artifacts: one orphaned factory + one orphaned paymaster exist on Base from a mis-keyed first run (signed by the heima prod deployer before
resolve_master_keybecame chain-aware) — functional but never referenced by any registry, env, or profile; ignore them on the explorer.
- Deployer wallet (EVM): the prod deployer —
grep ^DEPLOYER_ADDR_HEIMA scripts/operator-workstation.env; see the deployer table above for prod vs test. - v2 deploy date: 2026-05-19 · #164 E1 deploy date: 2026-06-02
- Compiler: Solc 0.8.20,
evm_version = "london"(aforge scriptheader-validation workaround, NOT Heima's EVM level — Heima executes Cancun; see AGENTS.md "Heima EVM compatibility level"). The EntryPoint v0.7 is the canonical eth-infinitism bytecode, deployed viaforge create. - Deploy script:
crates/agentkeys-chain/script/DeployAgentKeysV1.s.sol
Constructor wiring (verified post-deploy):
AgentKeysScope.registry()= the v2SidecarRegistry✓P256AccountFactory.entryPoint()= the v0.7EntryPoint✓,.k11Verifier()= the liveK11Verifier✓K3EpochCounter.currentEpoch()=1;.signerGovernance()= deployer (to be transferred to an M-of-N multisig)SidecarRegistry.ROLE_CAP_MINT()=1,ROLE_RECOVERY()=2,ROLE_SCOPE_MGMT()=4✓
# One-shot health check across the v2 set:
AGENTKEYS_CHAIN=heima bash scripts/utils/verify-heima-contracts.sh # exits 0 on all-pass
# Resolve addresses + RPC from the chain profile (#251 — never paste literals):
PROFILE=crates/agentkeys-core/chain-profiles/heima.json
EP=$(jq -r '.contracts[] | select(.name=="EntryPoint").address' "$PROFILE")
FACTORY=$(jq -r '.contracts[] | select(.name=="P256AccountFactory").address' "$PROFILE")
RPC=$(jq -r '.rpc.http' "$PROFILE")
# Bytecode presence (eth_getCode), e.g. the ERC-4337 EntryPoint:
cast code "$EP" --rpc-url "$RPC" | head -c 12
# View call, e.g. factory wiring:
cast call "$FACTORY" "entryPoint()(address)" --rpc-url "$RPC"The verify script checks, per contract: (1) bytecode presence (eth_getCode non-empty); (2) view functions return the expected constant (catches wrong-code-at-this-slot drift); (3) constructor wiring (AgentKeysScope.registry() → the deployed SidecarRegistry); (4) initialization (K3EpochCounter.currentEpoch ≥ 1, signerGovernance != address(0)). It reads addresses from operator-workstation.env, so changing AGENTKEYS_CHAIN picks up the chain-specific deployment.
Explorer note: heima.statescan.io is Substrate-side — it indexes pallet extrinsics/events but does NOT decode EVM calls/bytecode. EVM contract verification on Heima goes via direct RPC until agentkeys-specific indexing on Litentry's subscan-essentials fork ships (arch.md §22a.6).
bash scripts/operator/chain/heima-bring-up.sh is idempotent, by VERSION not bytecode:
- Skip when all four cores have on-chain code AND
crates/agentkeys-chain/VERSION== the chain profile'scontract_set_version(the recorded deployed version). - Redeploy when the stored address is the
0x0sentinel / absent or has no on-chain bytecode (chain reset). A bumpedVERSION≠ the recorded version is a hard stop that prints the mismatch and asks for an explicit opt-in (it orphans state + costs mainnet gas — see below) rather than auto-redeploying. - Force a fresh deploy at new addresses (contract patch): bump
crates/agentkeys-chain/VERSION, then re-run withFORCE_DEPLOY=1(blind) — or, for the #164 account-auth cutover, use the account-auth cutover helper (operator-internal; probes the livesetScopeselector + skips when already live).
On a fresh deploy the bring-up script auto-writes the chain profile (contracts[] + contract_set_version, step 6b — the source of truth) and operator-workstation.env (step 6). It does NOT touch this markdown — so update only the human prose here (the version line + any ABI/cutover/historical note) when the design or version changes; the addresses live in the profile, not a table here. Confirm the two mirrors agree: bash scripts/utils/check-deployed-contracts-sync.sh. No bytecode comparison anywhere — Solidity metadata + immutables make it unreliable, so the human-asserted VERSION is the comparison key.
Full ABIs in crates/agentkeys-chain/src/*.sol. The functions broker + workers + CLI read on hot paths:
SidecarRegistry (account-auth design, #164 E3 — live since the 2026-06-08 cutover; #225 E7 account-model + resetMaster)
registerFirstMasterDevice(bytes32 deviceKeyHash, bytes32 operatorOmni, bytes32 actorOmni, bytes32 k11CredId, bytes32 k11RpIdHash, uint256 k11PubX, uint256 k11PubY, uint8 roles)— sel0x93b14d7c; bootstrapsoperatorMasterWallet[operatorOmni] = msg.sender. #225 E7 account model: the embeddedK11Assertion selfAttestationwas DROPPED — the passkey proof is the account'svalidateUserOpover theuserOpHash(which commits this calldata). Rejects an EOAmsg.sender(MasterMustBeAccount) — the master must be the operator'sP256Account. First-master-ONLY (revertsDeviceAlreadyRegisteredonceoperatorMasterWallet[omni] != 0).registerAdditionalMasterDevice(bytes32 newDeviceKeyHash, bytes32 operatorOmni, bytes32 newActorOmni, bytes32 newK11CredId, bytes32 newK11RpIdHash, uint256 newK11PubX, uint256 newK11PubY, bytes attestation, uint8 newRoles, K11Assertion existingMasterAssertion)— requires existing master;msg.sender == operatorMasterWalletregisterAgentDevice(bytes32 deviceKeyHash, bytes32 operatorOmni, bytes32 actorOmni, bytes linkCodeRedemption, bytes agentPopSig)— master-only (msg.sender == operatorMasterWallet); agents getROLE_CAP_MINTonlyrevokeAgentDevice(bytes32 deviceKeyHash)— master-only (msg.sender == operatorMasterWallet[entry.operatorOmni])revokeMasterDevice(bytes32 targetDeviceKeyHash, K11Assertion[] recoveryAssertions)— M-of-N recovery quorum (recoveryThreshold[operator]); refuses to strand the operatorresetMaster(bytes32 operatorOmni)— #225 E7, owner-ONLY (the deployer captured at construction). Dev/recovery escape hatch: wipes the operator's whole device list + clearsoperatorMasterWallet/recoveryThreshold/operatorNonce, so a FRESHregisterFirstMasterDevicecan re-bind WITHOUT redeploying the set (needed because first-master-only makes the binding otherwise immutable). The daemon'sPOST /v1/master/resetcalls this viascripts/operator/chain/heima-reset-master.sh. EmitsMasterReset(operatorOmni, clearedMaster, deviceCount).getDevice(bytes32 deviceKeyHash) → DeviceEntry— viewisActive(bytes32 deviceKeyHash) → bool— hot-path view for workersoperatorMasterWallet(bytes32 operatorOmni) → address— auto-generated getterowner() → address— auto-generated getter (the deployer; the onlyresetMastercaller). Probingowner()is howheima-reset-master.shdetects a pre-0.3 registry (the call reverts / returns empty there).
setScope(bytes32 operatorOmni, bytes32 agentOmni, bytes32[] services, bool readOnly, uint128 maxPerCall, uint128 maxPerPeriod, uint128 maxTotal, uint32 periodSeconds)— sel0xd8e9e3c6; gated bymsg.sender == operatorMasterWallet[operatorOmni](the operator'sP256Account). No inline K11 tuple — biometric authorization is the 4337 account'svalidateUserOp.revokeScope(bytes32 operatorOmni, bytes32 agentOmni)— sel0xdcff8c5b; samemsg.sender == operatorMasterWalletgate.getScope(bytes32 operatorOmni, bytes32 agentOmni) → Scope— viewisServiceInScope(bytes32 operatorOmni, bytes32 agentOmni, bytes32 serviceHash) → bool— hot-path view
advanceEpoch()— signerGovernance-onlysetSignerGovernance(address newGov)— signerGovernance-only (handoff or rotation)currentEpoch() → uint256— auto-generated gettersignerGovernance() → address— auto-generated getter
append(bytes32 operatorOmni, bytes32 actorOmni, bytes32 serviceHash, uint8 opType, bytes32 payloadHash)— open append (any caller; gas is the spam-resistance)getEntries(bytes32 operatorOmni, uint256 offset, uint256 limit) → AuditEntry[]— paginated viewentryCount(bytes32 operatorOmni) → uint256— view
- New deploy on any chain — addresses are written automatically by
heima-bring-up.shto the chain profile (contracts[]+contract_set_version) +operator-workstation.env; this doc only needs a PROSE touch (the version line + a one-line note) if the design changed. No address table to edit. - Constructor re-wiring — any change to the deploy script's constructor args; re-record the "Constructor wiring" section
- K3 epoch advance —
currentEpochmonotonically increases; update the "Constructor wiring" line for the latest value signerGovernancetransfer — when handoff from deployer → operational signer (or → multisig in stage 2) happens, record the new address + tx hash- Re-deploy at fresh addresses — the chain profile is rewritten automatically; mention the old → orphaned addresses in the prose / "Historical deploys" section for the audit-trail (no table row to replace)
- Test redeploy — re-pin the addresses in
operator-workstation.test.env(the authoritative test record), then re-runscripts/operator/secrets/ci-set-github-secrets.shso theTEST_*secret copies follow; this doc's "Test / CI deploy" section needs only a prose note