Aegis Vault Documentation Audit
The docs read confidently and the on-chain story is detailed, but the surface contradicts itself across versions: docs, whitepaper, app, operator onboarding, and create flow each describe a slightly different protocol. Several footer/whitepaper-referenced doc URLs return empty bodies, and there is no agent-readable index. The whitepaper claims V4 ships on the date of this audit (2026-05-14), which may explain some of the transitional-state contradictions — but the docs site does not say so anywhere a reader would see it.
1. Factory address in the docs does not match the factory address in the whitepaper or the GitHub README (critical)
Location: /docs §04 vs /whitepaper §9.1 vs github.com/mdlog/aegis-vault README
Problem: The docs page lists AegisVaultFactory 0x75668Ca95aCaE419732B0c7AeA1ee7f9B2EFE0e3 as the live address on 0G Aristotle Mainnet. The whitepaper §9.1 — headed "V3 + Khalani stack deployed 2026-04-27 (canonical for new vaults)" and noting "V4 ships 2026-05-14" — lists AegisVaultFactoryV4 0x9e36520650Fd7d06CA77Fb0045456c03d3582A5F. The GitHub README also gives 0x9e36520650Fd7d06CA77Fb0045456c03d3582A5F as the factory. Both are presented to the reader as the live mainnet factory.
Consequence: Any integrator (or agent) wiring a frontend, SDK, or indexer off the docs page is talking to a different factory than the one the whitepaper and the source repo identify as the V4 canonical factory. New vaults created against the docs address may not be discoverable by tooling built against the whitepaper address, and vice-versa. If V4 is mid-cutover today, the docs page is stale; if it isn't, the whitepaper is wrong. Either way, the reader can't tell.
The fix: Pick the canonical V4 factory, replace the 0x75668… entry in /docs §04, label every contract row with its version (V3 / V4), and mark the V3 row as legacy if it is being kept for indexers. The docs page should be the source of truth and the whitepaper should reference it, not the other way around.
2. Policy enforcement description in user guide and operator onboarding both contradict the whitepaper's policy table (critical)
Location: /docs §06 step 05 vs /operator/register §R.03 vs /whitepaper §4.1
Problem: Three surfaces tell three different stories about the most safety-critical claim in the product — whether the smart contract will reject a position-size breach.
/docs§06 tells depositors thatmaxPositionBps, maxDailyLossBps, stopLossBps, confidenceThresholdBps, cooldownSeconds, maxActionsPerDayare "hard gates — the vault reverts if an executed trade would breach them."/operator/register§R.03 says "Max Position, Min Confidence, Cooldown, and Max Trades are sealed into the vault's on-chain policy and enforced by the contract on every execution. Stop-Loss is the only field below that stays off-chain." A "MAX POSITION SIZE HARD CAP 50%" badge sits on the same form./whitepaper§4.1's table marksmaxPositionBps,maxDailyLossBpsandstopLossBpsasOff-chain(orchestrator-enforced) and closes with: "Eleven of fifteen fields are enforced at the contract layer. The four size-based risk limits … are currently enforced by the orchestrator as pre-submission validation."
Consequence: A user reading the docs assumes the contract is their backstop. An operator reading /operator/register is told the same thing and asked to publish a "HARD CAP" as a marketing promise. The whitepaper says it is the orchestrator that enforces these, contract-side enforcement is for the other eleven fields only. If a misbehaving or compromised orchestrator submits an intent that exceeds maxPositionBps, two pages imply revert and the whitepaper implies execution. This is no longer "docs vs whitepaper" — it's two product surfaces vs the technical spec.
The fix: Reconcile to whatever ExecLib actually does. If maxPositionBps is off-chain, remove the "HARD CAP" badge from /operator/register and update /docs §06 to mark each field as "on-chain revert" or "orchestrator-only" using the same column the whitepaper §4.1 already uses. Link each row to the relevant Solidity source.
3. JaineVenueAdapter V1 and V2 are listed with the same address (critical)
Location: /docs §04
Problem: The contracts table reads:
JaineVenueAdapter (V2) 0xA4E2aeB9e1a5297DE38d7Ad8e11b1714ca481F2f
JaineVenueAdapter (V1, legacy) 0xA4E2aeB9e1a5297DE38d7Ad8e11b1714ca481F2f
The "V1, legacy" entry uses the V2 address verbatim — almost certainly a copy-paste typo rather than a deployment bug.
Consequence: Any tool that indexes legacy executions, builds a venue migration tracker, or attempts a V1→V2 reconciliation will get one address back for both versions and report no migration occurred. An integrator who explicitly tries to interact with the "legacy" adapter to handle stale state will route to the live V2 instead. Treated as critical because the contracts table is the integrator's source of truth — any error in it cascades.
The fix: Either supply the actual V1 address or remove the V1 row. If V1 is fully deprecated, say so explicitly with the deprecation block height.
4. Operator stake denomination contradicts itself across pages (critical)
Location: /docs §07 vs /whitepaper §5.2
Problem: Docs §07 says operator tiers are denominated in A0G — "Bronze 5K – 20K A0G", "Silver 20K – 50K A0G", "Gold ≥ 50K A0G". Whitepaper §5.2 explicitly argues that operator stake is not denominated in the native token and says: "Operator stake is denominated in the same asset as the vault base asset — USDC.e on 0G, canonical USDC on Arbitrum. This deliberately avoids the token-price-volatility games…" The whitepaper then lists five tiers (None / Bronze 1,000 / Silver 10,000 / Gold 100,000 / Platinum 1,000,000 USDC). The docs list three tiers + "Frozen" in A0G.
Consequence: An operator who attempts to register at "Bronze" using the docs numbers will deposit the wrong asset in the wrong amount and either fail or be quietly mis-tiered. The /marketplace page references "BRONZE+ / SILVER+ / GOLD+" filters — but there is no Platinum filter, so the UI seems to follow the docs, not the whitepaper. This is internally inconsistent enough that no operator can confidently onboard without reading source.
The fix: One canonical tier table, embedded in both /docs §07 and /whitepaper §5.2 from the same file. State the asset, decimals, contract field, and whether the cap is a UI hint or a require in OperatorStaking. Add the Platinum filter to /marketplace if Platinum is real.
5. Five whitepaper-referenced doc files all return empty bodies, plus an empty /api and /docs/api page (critical)
Location: /docs/MULTI_STRATEGY_RFC.md, /docs/V4_MIGRATION_GUIDE.md, /docs/V4_DEPLOYMENT_PLAN.md, /ARCHITECTURE.md, /HACKATHON_SUBMISSION.md, /api, /docs/api
Problem: Each of these returns HTTP 200 with body.innerText.length === 0 — the SPA shell renders but no document is mounted. The whitepaper §10.1 and §11 link to several of these as the authoritative migration and architecture references, and the footer's "API" link target (/api) is one of them.
Consequence: Migration guidance for V3→V4 — a transition the whitepaper says ships 2026-05-14, the date of this audit — is unreachable from the docs at the moment integrators would most need it. Anyone clicking the footer's "API" link lands on a blank page even though /docs §05 lists an HTTP API table elsewhere. Agents crawling for /docs/V4_MIGRATION_GUIDE.md get a 200 with no content and may cache emptiness as the answer.
The fix: Either publish these documents or remove the dead links from the whitepaper and footer. The footer "API" anchor should point to /docs#api-reference, not /api. While at it, return a real 404 (not a 200 with an empty body) for any unmounted markdown route — a 200 with empty content is the worst possible answer for crawlers.
6. No llms.txt, no llms-full.txt, no OpenAPI spec (significant)
Location: /llms.txt, /llms-full.txt, /api, /docs §05
Problem: Both llms.txt and llms-full.txt return the empty SPA shell — there is no agent-readable index of the documentation. The orchestrator HTTP API is documented only as a prose table in /docs §05 with no OpenAPI/Swagger spec linked, no example payloads, and no response schemas.
Consequence: An AI agent (Claude Code, Cursor, etc.) trying to use this protocol has no machine-discoverable surface. It has to fetch each docs page individually, parse the prose table, and guess response shapes for /api/journal, /api/market/summary, /api/og-compute/models, etc. Mutation endpoints (POST /api/cycle) are listed without a request body schema or auth example.
The fix: Publish a real llms.txt that enumerates the docs subpaths plus a llms-full.txt that concatenates the docs body. Ship an OpenAPI 3 spec for the orchestrator HTTP surface and link it from /docs §05 — the prose table is already a near-OpenAPI in disguise.
7. cbBTC appears as a supported asset in the create-vault flow but nowhere else (significant)
Location: /create step 04 vs /docs §06 vs /faucet
Problem: The Create-vault "Live Preview" panel lists "ASSETS 4 — USDC.e, WETH, WBTC, cbBTC". /docs §06 step 03 says "USDC.e, WETH, WBTC are all supported on 0G" — no cbBTC. The /faucet "canonical token addresses" list shows USDC.e, WETH, WBTC, W0G — no cbBTC. There is no cbBTC contract address published anywhere on the site.
Consequence: Users picking cbBTC in the create flow will hit an undocumented (and possibly non-existent on 0G) token contract. Without a canonical address listed on /faucet, there is no way for a user to import cbBTC into MetaMask before depositing.
The fix: Either remove cbBTC from the create flow preview, or add its canonical 0G address to /faucet and add it to the supported asset list in /docs §06.
8. API key authentication on a fund-moving mutation endpoint is referenced but never explained (critical)
Location: /docs §05
Problem: The API reference says "Mutations require either localhost origin or an API key via x-api-key." That's the entire treatment of auth. There is no documentation of how to obtain a key, what format it takes, whether it can be rotated, scope, expiry, or which endpoints accept it. POST /api/cycle is the marked "Auth required" endpoint and it triggers the AI decision cycle that moves vault funds.
Consequence: Anyone running an orchestrator past localhost has no way to safely expose POST /api/cycle to a remote caller. An agent generating client code will emit a header but cannot tell the user how to fill it. For a vault protocol that moves real USDC.e on mainnet, leaving the only documented auth mechanism on the only fund-moving endpoint at a single sentence is a security-documentation gap, not a polish gap.
The fix: Document key issuance (env var? config file? operator dashboard?), expected format, rotation, scope, and the full list of endpoints that honor it. A one-paragraph "Authentication" subsection under §05 would close this gap.
9. Version metadata and contract counts are inconsistent across four surfaces (significant)
Location: homepage footer, homepage badge row, homepage "Deployment Footprint" panel, /docs, /whitepaper page header, /whitepaper body
Problem:
- Homepage footer: "VERSION 2.0 · COMMIT 24623da".
- Homepage badge row: "18 CONTRACTS · 0G MAINNET".
- Homepage "Deployment Footprint" panel: four contracts shown (AEGISVAULTFACTORY, OPERATORREGISTRY, AEGISGOVERNOR, SEALEDLIB).
/docsheader: "DOCUMENTATION · V1.0". §04 lists eleven addresses./whitepaperpage header: "TECHNICAL WHITEPAPER · V1.0 · 2026-04"./whitepaperbody line 1: "Version 1.3 · 2026-04-27"./whitepaper§2.1: "sixteen live contracts"./whitepaper§9.1: a different set of V4 addresses (≥11 visible before the scraped table is truncated mid-row atOperatorReputation).
So: four different contract counts (4 / 11 / 16 / 18) and three independent version labels (Site 2.0 / Docs 1.0 / Whitepaper page 1.0 vs body 1.3). The whitepaper §9.1 also says "V4 ships 2026-05-14" — the date of this audit — meaning the reader cannot tell whether they are looking at the V3 end-state, the V4 day-of cutover, or a stale snapshot.
Consequence: A reader cannot determine which copy of the docs/whitepaper they are looking at, which version of the protocol it describes, or how many contracts are actually deployed. Agents that index by version label will get conflicting signals.
The fix: One canonical "Site version / Docs version / Whitepaper version / Protocol stack version (V3 / V4) / Contract count" block, generated from the same source, included on every page header. Today these are independent string literals across at least six places. Surface the V4 cutover date prominently on /docs so readers know whether the page is pre- or post-transition.
10. The "Faucet" route does not provide a faucet (significant)
Location: homepage footer link → /faucet
Problem: The footer advertises "Faucet" as a product link. The page itself begins "Real tokens on 0G Aristotle Mainnet… No faucet — these are real canonical assets without a public mint()."
Consequence: Anyone clicking "Faucet" because they need testnet-style funds is told there isn't one. The page is genuinely useful as a token-address reference, but its name promises something else. Agents that look for a faucet by route name will surface a misleading match.
The fix: Rename the route/link to "Tokens" or "Get USDC.e", and surface the actual "0G faucet for gas" instructions (the docs mention the faucet if you only need to test for A0G but never link to it).
11. Operator slashing percentages disagree across docs and whitepaper (significant)
Location: /docs §07 vs /whitepaper §5.4
Problem: Docs §07 says "Silver — 20% slash risk" and "Slashing forfeits 10–50% o…" (text appears to be truncated). Whitepaper §5.4 says MAX_SLASH_BPS = 5000 — i.e. up to 50% per single slash() call, governor-only, with the same 50% cap over a rolling window. There is no tier-specific slash cap in the whitepaper.
Consequence: A prospective operator reading the docs sees a tier-specific cap (Silver = 20%). The whitepaper says the cap is enforced contract-side at 50% irrespective of tier. The actual at-risk amount for a Silver operator is potentially 2.5× higher than the docs imply.
The fix: Replace the tier-specific slash percentages in /docs §07 with the contract-enforced cap. If there is an additional governance-tier policy that further restricts slashes, document it and cite the governor proposal that sets it.
12. Arbitrum mirror is documented in the whitepaper and README but has no addresses anywhere on the docs site (significant)
Location: /docs §04 vs /whitepaper §5.2 vs GitHub README
Problem: The GitHub README says "The system also mirrors to Arbitrum One with identical V4 bytecode." Whitepaper §5.2 says "Operator stake is denominated in the same asset as the vault base asset — USDC.e on 0G, canonical USDC on Arbitrum." /docs §04 lists only 0G Aristotle Mainnet (chain id 16661) addresses. Nowhere on the docs site is there an Arbitrum address for the factory, the operator staking contract, or USDC.
Consequence: An operator who reads the whitepaper, decides to stake on Arbitrum using canonical USDC, and goes to the docs to find the contract address has nowhere to go. The "dual-chain real execution" promise in the whitepaper subhead is undocumented on the integrator-facing page.
The fix: Add an "Arbitrum One" subsection to /docs §04 with at minimum the factory, operator staking, and venue adapter addresses, plus the canonical USDC address used for stake. If the Arbitrum mirror is not yet live, mark it explicitly as "planned" so readers don't waste time hunting.
13. Live state widgets disagree with each other on veto count vs veto rate (minor)
Location: homepage hero vs /app §A.03 vs /app §A.05
Problem: Homepage hero says "EXECUTED ACTIONS 1 · VETO RATE 0.0%". /app §A.03 reads "EXECUTIONS 1 · BLOCKED 1 · BLOCKS / SKIPS 1 / 65". /app §A.05 reads "SIGNALS 100 · HITS 100 · VETO 0 · ROLLING ACCURACY 100%". Three different denominators (lifetime / lifetime / 30D) reporting three different veto stories — including one (0/100) that flatly disagrees with "BLOCKED 1" on the same page.
Consequence: The homepage is the first impression of how the policy gate actually behaves. Three different live-state numbers on three different surfaces undermine the "Don't trust. Verify." trust-model copy. An agent ingesting these to summarize protocol health will pick one at random.
The fix: Pick one canonical event source (the on-chain journal) and derive every widget from it with a labelled window (e.g. "lifetime" vs "30D"). At a minimum reconcile §A.03 BLOCKED with §A.05 VETO.
14. Audit scope limitation: several footer links not verified (minor)
Location: homepage footer (Architecture, Trust model, Contracts anchors)
Problem: This audit verified that /api, /docs/api, llms.txt, llms-full.txt, and the five whitepaper-linked markdown files all return 200-with-empty-body. The footer also exposes "Architecture", "Trust model", and "Contracts" anchors that were not crawled in the scraped evidence.
Consequence: Given the pattern (/api is dead, five markdown files are dead), there is reasonable cause to suspect additional footer links may be unmounted. The team should not rely on this audit as exhaustive.
The fix: Run the team's own link checker over every footer anchor. Whatever route the homepage advertises as a product or developer surface must return real content, or the anchor should be removed.
What they do well
- The contract-level architecture write-up (§2.1, §4.1 policy table, §5.4 slashing caps in the whitepaper) is unusually concrete — typehashes, BPS constants, governor-only mutators all named.
- The "live state is genuine emptiness, not seed data" framing on
/governanceand/marketplaceis honest and avoids fake-TVL marketing copy. /docs§05 has a single-table API surface that, while prose-only, is at least exhaustive — every orchestrator endpoint is listed.
Top 3 recommendations
- Reconcile the contract addresses, the version labels, the contract counts, and the policy-enforcement story across docs / whitepaper /
/operator/register/ GitHub README — generate them from one source. Make the V4 cutover date visible on/docs. - Either publish or remove the five empty whitepaper-linked docs (
MULTI_STRATEGY_RFC.md,V4_MIGRATION_GUIDE.md,V4_DEPLOYMENT_PLAN.md,ARCHITECTURE.md,HACKATHON_SUBMISSION.md) and fix/apiso it doesn't 200 with an empty body. Add Arbitrum addresses or mark the mirror "planned." - Ship
llms.txt,llms-full.txt, and an OpenAPI spec for the orchestrator HTTP API — and write a real Authentication subsection that explains how anx-api-keyis issued, scoped, and rotated for the fund-movingPOST /api/cycleendpoint.