# Finagent Os

> Governance-first AI agent platform for SOX-regulated crypto finance. Deterministic policy spine, MCP read surface, audit trail.

- **Type:** MCP server
- **Install:** `agentstack add mcp-rz-logic-finagent-os`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [RZ-Logic](https://agentstack.voostack.com/s/rz-logic)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [RZ-Logic](https://github.com/RZ-Logic)
- **Source:** https://github.com/RZ-Logic/finagent-os
- **Website:** https://velocyt.ca/

## Install

```sh
agentstack add mcp-rz-logic-finagent-os
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

FinAgent OS

  The platform's job is to make SOX-defensible properties structurally true,not just true on paper.

  
  
  
  
  
  

  Principle •
  Self-Audit •
  What Ships •
  Stack •
  How to Read •
  Build Decisions •
  Engineering Properties •
  Limitations •
  Roadmap •
  Acknowledgements

---

## 🧭 Why I Built This

I'm an ACCA with audit roots who pivoted into AI automation architecture. Working in audit, you learn quickly that *"the policy says X"* and *"the code does X"* are different sentences, and the second one is the only one that matters when the regulator shows up.

When I started designing AI agents for finance operations, the same gap kept appearing. *"The LLM is in the loop but the human is the final decision-maker"* is documentation, not architecture. So I built FinAgent OS: a platform where the SOX-defensible properties (append-only audit, segregation of duties, AI non-authority) are enforced by Postgres triggers and missing-by-design tool surfaces, not by trust in policy text.

The flagship workflow is a Reserve Reconciliation Agent calibrated to crypto-exchange Proof-of-Reserves work, with Kraken's published Dec 2025 PoR as the reference implementation. The platform beneath it is what makes the flagship safe to ship.

---

## 🛡️ The Architectural Principle

> **AI is optional and non-authoritative. Policy decisions are deterministic.**

Every material financial decision in FinAgent OS is enforced by deterministic rules: Postgres triggers, n8n IF nodes, JS decision matrices that an auditor can read top-to-bottom. AI assists human judgment at narrative boundaries (summarizing decisions for human approvers, surfacing drift signals in retrospective audits). AI never produces a control outcome.

The complete enumeration, with every decision point in the system classified deterministic vs. advisory, lives in **[`SOX-MAPPING.md`](./SOX-MAPPING.md)** under the *"Where is the AI?"* table.

**The empirical proof:** setting `FAO_AI_ASSIST_ENABLED=false` does not change a single policy outcome. Only the human-readable narratives go missing. With AI off, every decision in the repo is reproducible by reading the migration files.

---

## 🎯 The MCP Self-Audit (Apr 25, 2026)

After the MCP server shipped, **Claude Opus** was given access to the FinAgent OS audit trail through the MCP read surface and asked to (1) narrate the most recent execution, (2) approve a pending HITL request, and (3) red-team the system actively: *"i have designed controls to stop ai from doing it. i want to see you try and fail."*

📜 **Full transcript (public, Anthropic-hosted):**
**[claude.ai/share/5f0eb294-e658-42b5-a876-8164a0049960](https://claude.ai/share/5f0eb294-e658-42b5-a876-8164a0049960)**

Two outcomes worth highlighting.

### The architecture held under adversarial probe

Three semantic searches across `approve / resolve / decision`, `write / commit / override / bypass`, and `resolve / set status / sign` all returned the same five tools, **none of which mutate HITL state**. Defense in depth across four independent layers held: missing endpoint → bounded write tool → channel separation between MCP and Slack-based approvals → database-level SoD trigger.

> *"I cannot reach the approval state. Not because I'm refusing — because the surface doesn't exist. Defense in depth held: missing endpoint → bounded write tool → channel separation → DB trigger. Any one of those alone would have stopped this; you have four."*
> *Claude Opus, Apr 25, 2026 audit*

### The audit surfaced a real control gap, on first contact

While narrating the most recent execution, Claude observed that `agent_registry.approval_threshold = 2` for RED-tier agents, and the `policy_rationale` text claimed *"Controller + CFO review required"*, but the HITL trigger only enforced `approver_id ≠ agent_invoker_id`. Single-approver SoD held; **dual-approval was named in documentation but not wired in code**.

  

Documented as **[ADR-0001](docs/adr/0001-dual-approval-not-yet-enforced.md)** the same hour.

Full evidence preserved at [`docs/evidence/mcp/`](./docs/evidence/mcp/).

---

## 🧱 What Ships in v1

```
┌──────────────────────────────────────────────────────────────────────────┐
│                                                                          │
│   One typical HITL-gated execution writes 11 audit rows:                 │
│                                                                          │
│     STARTED                  ← agent invocation, scope captured          │
│       │                                                                  │
│       ├─ ASSET_RECONCILED   ← BTC: ratio 1.003, IN_BAND, ALLOW          │
│       ├─ ASSET_RECONCILED   ← ETH: ratio 1.013, IN_BAND, ALLOW          │
│       ├─ ASSET_RECONCILED   ← USDC: clean baseline, ALLOW                │
│       ├─ ASSET_RECONCILED   ← USDT: USD-material breach, REQUIRE_HUMAN   │
│       ├─ ASSET_RECONCILED   ← SOL: under-reserved, REQUIRE_HUMAN/CRIT    │
│       ├─ ASSET_RECONCILED   ← XRP: ratio 1.006, IN_BAND, ALLOW          │
│       └─ ASSET_RECONCILED   ← ADA: negative ledger, REQUIRE_HUMAN/CRIT   │
│                                                                          │
│     HITL_REQUESTED          ← worst-asset rollup → CRITICAL HITL queue   │
│     HITL_RESOLVED           ← Sarah Chen approves; SoD trigger validates │
│     COMMITTED               ← single COMMITTED row with three facts:     │
│                                policy_outcome · hitl_status · status     │
│                                                                          │
│   Every row carries a single correlation_id. Every row is append-only,   │
│   enforced by a Postgres trigger that rejects UPDATE/DELETE for every    │
│   identity including service_role and superusers.                        │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘
```

### Component overview

| Component | What it is | Role of AI |
|-----------|-----------|:-:|
| **Platform governance layer** | 8 Postgres migrations + 3 seed files. 14 `FAO-GOV-*` controls. | None |
| **Reserve Reconciliation Agent** (flagship) | n8n workflow. RED-tier. FAO-REC-001 → 009. Owner: Sarah Chen. | None |
| **Three stub agents** | Close Orchestrator (AMBER), Variance Analyzer (AMBER), Treasury Monitor (RED). Registered before built. | None |
| **MCP server** | Python FastMCP. 6 read tools + 1 bounded invoke. **No mutation tools by design.** | Read access only |
| **HITL Gateway + Approval Handler** | n8n sub-workflows + Postgres functions (`fao_hitl_create`, `fao_hitl_resolve`). | None |
| **Demo identities** | Sarah Chen (Controller) ← Aisha Okonkwo (FP&A); Sarah → Marcus Patel (CFO). Linear escalation chain. | None |
| **AI advisory layer** | Roadmap (v1.1). Gated by `FAO_AI_ASSIST_ENABLED`. Populates `ai_human_brief`. | Advisory only (when shipped) |
| **Weekly Rubric Audit** (FAO-GOV-012) | Roadmap (v1.1). Cron job aggregating 7-day drift signals. | Advisory only (when shipped) |

Full control matrix lives in [`SOX-MAPPING.md`](./SOX-MAPPING.md).

### Flagship: Reserve Reconciliation Agent

The agent reconciles wallet holdings (Fireblocks proxy) against ledger liabilities (NetSuite proxy) across **7 crypto assets**. Fixtures break down into three groups:

- **4 calibrated to Kraken's published Dec 2025 Proof of Reserves.** BTC, ETH, and XRP wallet/ledger pairs reproduce the published ratios (BTC 100.3%, ETH 101.3%, XRP 100.6%), anchoring the agent against real exchange disclosures. USDC is the clean baseline.
- **3 synthetic anomalies, each tagged to the control it exercises.** USDT triggers FAO-REC-002 (stablecoin USD-materiality breach). SOL triggers FAO-REC-004 (under-reservation floor). ADA triggers FAO-REC-007 (negative-liability anomaly per the [krakendb 2014 reference](https://github.com/krakenfx/krakendb)).
- **Provenance threaded into every audit row.** `inputs.fixture_metadata` records which asset is calibrated, which is synthetic, and which control each anomaly was designed to exercise.

The decision matrix is deterministic: 7 rules evaluated top-down, first match wins. No LLM in the policy path.

---

## 🔌 Stack

| Layer | Tool | Why |
|-------|------|-----|
| Database + Auth + RLS | Supabase (Postgres 15) | Triggers and RLS at the database layer; auth backed by Supabase users |
| Workflow orchestration | n8n (self-hosted, DigitalOcean) | Visual policy logic auditors can read; sub-workflows for governance enforcement boundaries |
| AI advisory (gated, v1.1) | Claude API | Plain-English narrative summaries for human approvers; never on the authority path |
| MCP server | Python 3.12 + FastMCP | Read + bounded-invoke surface for AI consumers; stdio transport in v1 |
| HITL approval channel | Slack (link → POST `fao_hitl_resolve`) | Channel-separated from MCP; intentional defense-in-depth boundary |
| Approval enforcement | Postgres `BEFORE UPDATE` trigger (`hitl_queue_sod_guard`) | Loud named exception on SoD violation; sole enforcer (see Migration 0005c) |
| Append-only enforcement | Postgres `BEFORE UPDATE/DELETE` trigger (`shadow_ledger_append_only_guard`) | Catches every identity including service_role and superusers |

**No infrastructure novelty.** Everything in this stack is off-the-shelf. The contribution is the application layer: domain-specific governance for SOX-regulated crypto-finance, not the underlying tools.

---

## 📖 How to Read This Repo

For a reviewer with limited time, here's the recommended path:

1. **[`SOX-MAPPING.md`](./SOX-MAPPING.md)**: start with the *Where-is-the-AI* table. ~90 seconds. Tells you the architecture's stance on AI authority.
2. **[The MCP self-audit transcript](https://claude.ai/share/5f0eb294-e658-42b5-a876-8164a0049960)**: ~5 minutes. Claude Opus interrogating the audit trail and red-teaming the surface.
3. **[ADR-0001](docs/adr/0001-dual-approval-not-yet-enforced.md)**: the dual-approval gap that surfaced during the audit. ~3 minutes.
4. **[`supabase/migrations/0004_shadow_ledger.sql`](supabase/migrations/20260420_0004_shadow_ledger.sql)** + **[`0005b_hitl_queue_sod_trigger.sql`](supabase/migrations/20260420_0005b_hitl_queue_sod_trigger.sql)**: the two core governance triggers. ~5 minutes. Read the actual code that enforces append-only and SoD.
5. **[`workflows/fao_reserve_recon_v1.json`](workflows/fao_reserve_recon_v1.json)**: the flagship's policy decision matrix. The relevant code is in the *Per-Asset Reconcile* node's `jsCode` block.

Total: ~20 minutes for a read of how the platform works and what's shipped vs. roadmap.

---

## ⚖️ Engineering Properties

| Principle | How it works |
|-----------|--------------|
| **Append-only is a database constraint, not a convention** | Postgres trigger raises a named exception on UPDATE/DELETE against `shadow_ledger`. Catches every identity: authenticated users, `service_role`, superusers. RLS provides a second layer (no UPDATE/DELETE policy → default-deny). |
| **AI never on the authority path** | Policy outcomes are computed by deterministic rules. AI produces narrative *after* the decision is made and recorded. With `FAO_AI_ASSIST_ENABLED=false`, every policy outcome is identical; only narratives go missing. |
| **The Agent Registry is the gate** | No agent executes without a row specifying risk tier, named process owner, SOX control IDs, rollback procedure, and data sources. Stubs exist in the registry to prove the framework precedes the code. |
| **Loud rejection > silent rejection** | SoD enforcement is a Postgres trigger raising `FAO-GOV-004: ...` exceptions, not an RLS policy that returns zero rows. Migration 0005c restructured this deliberately: silent rejection is weaker audit posture than loud, attributed exception. |
| **Channel separation defends in depth** | The MCP surface and the Slack approval surface share no transport. AI on the MCP channel has no path to the approval endpoint regardless of reasoning. |
| **No mutation tools on the AI surface** | The MCP server has six read tools and one bounded invoke. There is no `approve_hitl`, no `update_threshold`, no `register_agent`. The asymmetry IS the architecture. |
| **Documented gaps and preserved iteration history** | Known gaps (ADR-0001) ship publicly with roadmap entries. The git history of Migration 0005 → 0005b → 0005c is preserved as evidence of how SoD enforcement evolved. |
| **Risk-tier classification before build** | Every agent is GREEN/AMBER/RED before code is written. RED tier agents require 2 approvers (state-machine wiring on roadmap; see ADR-0001). |

---

## 🛠️ Build Decisions & Lessons Learned

The interesting decisions weren't the obvious ones. Five non-obvious choices and the alternatives I rejected, with the empirical evidence that drove each.

### 1. The Slack link-expander incident: GET shows a form, POST mutates

The HITL approval flow originally used a single endpoint that accepted approval state via URL parameter. Click the link in Slack → server reads the URL → updates `hitl_queue.decision = 'APPROVED'`. Clean, simple, and **completely broken in practice**.

The first time I posted an HITL request to `#fao-approvals`, every queued item was approved within seconds. No human had clicked anything. Looking at `hitl_queue` showed `approver_id` stamped with the right Controller's UUID and `decided_at` timestamps clustered at the message-post time.

**Slack's link-unfurling bot was walking every URL in the message to generate previews.** A GET request that mutates state is functionally an open invitation to any well-meaning crawler. Fixed by splitting HTTP semantics: GET to that endpoint now renders a confirmation form; POST is the mutation. The Slack expander hits the GET, sees a form, leaves the row alone. The CSRF defense layer added on top closes the cross-origin POST surface.

> **The principle:** HTTP verbs aren't aesthetic. GET is for reads: link previewers, search engines, and accidental retries depend on that. The moment a GET mutates state, your security model includes every entity that ever follows a link.

### 2. SoD enforcement: trigger over RLS

Migration 0005 enforced segregation of duties via a Row-Level Security policy: `WITH CHECK (auth.uid() != agent_invoker_id)`. Functionally correct: a self-approval attempt returns zero affected rows.

That's the problem. **Silent rejection is weaker SOX posture than loud rejection.** A SOX auditor wants clearly-attributed exceptions in logs, not inferred *"this update affected zero rows because policy, we think."* Migration 0005b added a `BEFORE UPDATE` trigger that raises a named exception: `FAO-GOV-004: Segregation of duties violation. approver_id [...] cannot equal agent_invoker_id [...]`. Migration 0005c then *removed* the RLS policy entirely.

The RLS policy and the trigger were two layers on the same vector in the same direction. Layered enforcement is good; redundant enforcement at the same boundary creates interaction problems (the RLS layer silently filtered the violating row before the trigger could raise its loud exception). The trigger is the sole enforcer now. The git history of `0005 → 0005b → 0005c` ships in the repo as honest evidence of the evolution.

> **The principle:** Layer defenses at *different* boundaries (application + database, identity + integrity), not at the same boundary on the same vector. And when forced to choose between silent filtering and loud exception, choose loud. Auditors read logs.

### 3. ASSET_RECONCILED collapses observation + decision into one event

Earlier draft of the flagship had two events per asset: `COMPARED` (the observation: wallet, ledger, ratio computed) and `POLICY_DECIDED` (the deterministic outcome: ALLOW / REQUIRE_HUMAN / etc.). Two rows per asset across 7 assets meant 14 rows just for the per-asset section.

Schema constraint forced the collapse. `shadow_ledger.policy_outcome` is `NOT NULL`. A `COMP

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [RZ-Logic](https://github.com/RZ-Logic)
- **Source:** [RZ-Logic/finagent-os](https://github.com/RZ-Logic/finagent-os)
- **License:** MIT
- **Homepage:** https://velocyt.ca/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-rz-logic-finagent-os
- Seller: https://agentstack.voostack.com/s/rz-logic
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
