aureon-os / docs /SAAS_PLATFORM.md
GaryLeckey's picture
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 9)
002f18c verified
|
Raw History Blame Contribute Delete
23 kB
# Aureon SaaS Platform β€” connected Β· working Β· categorized
> The platform layer that turns the 715-module organism into a coherent
> product surface. It **surfaces** the repo β€” it does not rewrite it. Every
> system is categorized, every domain has a canonical entry point, and health
> is reported **honestly** (degraded where deps are missing, not everything-green).
This is **Phase 6A** of the production program. It builds on the
[Production-Grade Program](runbooks/PRODUCTION_GRADE.md) (the two-tier gate and
serving surface) and the [Aureon Operator Switchboard](architecture/AUREON_OPERATOR_SWITCHBOARD.md)
(the grounded cognition front door), reusing both rather than duplicating them.
---
## The shape of the problem
Three things already existed and only needed connecting:
| Layer | Already present | Role |
|-------|-----------------|------|
| **Tenant / auth / billing** | Supabase backend (`supabase/`) β€” auth, RBAC, per-user encrypted exchange keys, prepaid "gas-tank" credits, ~101 edge functions, 58 migrations | The multi-tenant foundation. Reused, not rebuilt. |
| **Catalog UI** | React console (`frontend/`, Vite + TS + shadcn) grouped by the 6 product domains | Already a systems console β€” but **starved of data** (the manifest JSONs it fetches didn't exist). |
| **Categorizer** | `SystemRegistry` (`aureon/command_centers/aureon_system_hub.py`) β†’ 12 capability categories Γ— per-module metadata | The classification engine. |
| **Health surfaces** | `SystemCoordinator.get_coordination_state()`, `get_operational_core().get_health()` | Live status inputs. |
**The gap:** there was no unified Python HTTP gateway producing a categorized
catalog + live status to feed the frontend, and nothing bridging the
single-tenant Python engine to the Supabase tenant layer. `aureon/saas/` is
exactly that bridge.
---
## Three taxonomies, reconciled
The repo describes itself three ways. The catalog maps all three onto each other:
```
6 product domains 24 filesystem domains 12 capability categories
(what the console groups) (folders under aureon/) (SystemRegistry semantics)
───────────────────────── ───────────────────────── ──────────────────────────
trading ◄────── trading, exchanges, ┐ Exchange Clients
accounting scanners, strategies … β”‚ Market Scanners
research β”œβ”€β”€β–Ί Execution Engines
cognition ◄────── operator, cognition, β”‚ Neural Networks
security queen, bots_intelligence β”‚ Stargate & Quantum
self-improvement … β”˜ Codebreaking & Harmonics Β· …
```
- **Product domain ← filesystem domain**: `domains._FS_TO_PRODUCT` (unmapped β†’ `self-improvement`).
- **Capability category ← module**: `SystemRegistry` classification, or the
stdlib keyword classifier (`catalog._CATEGORY_RULES`) when the registry can't load.
---
## Package layout β€” `aureon/saas/`
| Module | Responsibility | Key entry points |
|--------|----------------|------------------|
| `catalog.py` | **Categorized.** Build the normalized catalog; emit frontend manifests. | `build_catalog()`, `write_frontend_manifests()` |
| `domains.py` | **Connected.** Canonical entry point per domain + cheap reachability probe. | `domain_report()`, `probe_domain()`, `product_domain_for()` |
| `status.py` | **Working.** Honest health rollup, no heavy boot. | `get_platform_status()` |
| `gateway.py` | HTTP surface + optional Supabase tenancy bridge. | `register_saas_routes(app)`, `verify_supabase_jwt()` |
### `catalog.py` β€” categorized
`build_catalog(workspace=REPO_ROOT)` prefers the repo's `SystemRegistry` (richer
metadata: LOC, dashboard/thought-bus/Queen flags). If the registry can't import
β€” e.g. `psutil` absent in a lean environment β€” it falls back to a **self-contained
stdlib scanner** (`_scan_builtin`) that walks `aureon/` and classifies by keyword
rules. **The catalog never collapses to zero categories**; production always
categorizes. Result is cached to `state/aureon_saas_catalog.json` (gitignored).
`write_frontend_manifests()` emits the two JSONs the React console already fetches,
shaped to the frontend's TypeScript interfaces:
- **`aureon_saas_system_inventory.json`** β€” every system as a *surface* with
`id Β· path Β· name Β· kind Β· domain Β· purpose Β· owner_subsystem Β· wiring_status Β·
safety_class Β· auth_requirement Β· called_apis`, grouped by product domain.
- **`aureon_organism_runtime_status.json`** β€” one pulse per product domain
(status + system_count + categories), live when a status snapshot is passed in.
**Manifest ownership (post-unification):** the checked-in static copies in
`frontend/public/` are owned by the repo's manifest pipeline
(`scripts/validation/generate_*`), which emits a richer schema
(`schema_version`, `entrypoint_status`, `imports/exports`, `runtime_route`, …).
The gateway serves its own live-rendered manifests at `GET /api/manifests/<name>`
and does **not** overwrite the static files unless
`AUREON_WRITE_STATIC_MANIFESTS=1` is set (boot write and
`POST /api/manifests/refresh` both honour this). The console fetches live first
and falls back to the static copies.
### `domains.py` β€” connected
Each of the 24 filesystem domains gets a canonical entry point in `_ADAPTERS`
(`get_operational_core`, `get_queen`, `run_operator`, `AureonCognition`,
`get_feed_hub`, …). `probe_domain()` checks reachability with
`importlib.util.find_spec` β€” **an import-spec check, no construction** β€” so the
report is cheap and side-effect-free. Domains without a clean singleton fall back
to "is the package importable".
### `status.py` β€” working (honestly)
`get_platform_status()` composes per-domain reachability + the operational core's
own health snapshot + (if present) coordinator state, and rolls reachability up
into per-product-domain status (`ready` / `degraded` / `down`). It carries an
explicit **`note`**: reachability is import-level β€” a domain can be reachable yet
run degraded (e.g. optional `numpy` missing). Nothing is masked green.
---
## HTTP API
Mounted onto the operator's Flask app by `register_saas_routes(app)`, so **one
service on port 8790** serves operator + cognition + the SaaS catalog behind the
same security envelope (bearer auth / rate-limit / metrics / health from
[PRODUCTION_GRADE.md](runbooks/PRODUCTION_GRADE.md)).
| Method | Route | Returns |
|--------|-------|---------|
| `GET` | `/api/catalog` | Categorized catalog (12 categories Γ— domains Γ— systems). |
| `GET` | `/api/domains` | Product domains + per-domain reachability report. |
| `GET` | `/api/domains/<domain>` | One domain: entry point + its systems (capped). |
| `GET` | `/api/status` | Live platform health (honest, often degraded). |
| `GET` | `/api/organism` | Connectome coverage + recent breaths + mesh membership + unification telemetry. |
| `GET` | `/api/cognition` | The whole cognitive substrate + data provenance + truth roll-up. See [`architecture/COGNITIVE_SAAS.md`](architecture/COGNITIVE_SAAS.md). |
| `GET` | `/api/cognition/<part>` | One cognitive surface: `field` Β· `bus` Β· `mycelium` Β· `connectome` Β· `brain`, each provenance-stamped. |
| `GET` | `/api/consciousness` | The consciousness organs, categorized (self-perception Β· selfhood Β· purpose Β· governance Β· workforce Β· body), each with its route, safety posture & live truth_status. See [`architecture/CONSCIOUSNESS_CATALOG.md`](architecture/CONSCIOUSNESS_CATALOG.md). |
| `GET` | `/api/automation` | Progress toward "the whole repo, fully automated" β€” one honest % decomposed by dimension (connectivity Β· integration Β· consciousness Β· surfacing) + by category, composed from real coverage only. See [`architecture/AUTOMATION_INDEX.md`](architecture/AUTOMATION_INDEX.md). |
| `GET` | `/api/manifests/<name>` | A frontend manifest, rendered live (404 lists available names). |
| `POST` | `/api/manifests/refresh` | Rebuild the catalog; static files rewritten only with `AUREON_WRITE_STATIC_MANIFESTS=1`. |
---
## Tenancy bridge (optional, off by default)
The Supabase backend already owns auth/RBAC/billing. The gateway bridges to it
**without a new dependency**: when `AUREON_SUPABASE_JWT_SECRET` is set, the
`/api/*` routes additionally require a valid Supabase JWT, verified with a
stdlib HS256 check (`verify_supabase_jwt` β€” signature + `exp`). The verified
`sub` (user id) is stashed on `flask.g.tenant` for downstream scoping.
When the secret is **unset the bridge is disabled** (dev/offline unchanged),
layered *on top of* the operator's own bearer/rate-limit envelope β€” the two are
independent gates.
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
React console ──JWT──►│ operator service :8790 β”‚
(Supabase session) β”‚ β”œ security envelope (bearer/rate-limit) β”‚
β”‚ β”œ [optional] Supabase JWT bridge β”‚
β”‚ β”œ operator + cognition routes β”‚
β”‚ β”” SaaS routes β†’ catalog/domains/status │──► the 715-module
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Python organism
(surfaced, not rewritten)
```
---
## Verify
```bash
pip install -e '.[operator,dev]'
ruff check aureon/saas/ && mypy aureon/saas/
AUREON_LLM_OFFLINE=1 pytest tests/test_saas_catalog.py tests/test_saas_gateway.py -q
# catalog + manifests
AUREON_LLM_OFFLINE=1 python -c "from aureon.saas.catalog import build_catalog, write_frontend_manifests; c=build_catalog(); print('categories', c['category_count'], 'systems', c['total_systems']); print('manifests', [p.split('/')[-1] for p in write_frontend_manifests()])"
# gateway routes (offline)
AUREON_LLM_OFFLINE=1 python -c "from aureon.operator.operator_server import create_app; c=create_app().test_client(); print('catalog', c.get('/api/catalog').status_code, 'status', c.get('/api/status').status_code, 'domains', len(c.get('/api/domains').get_json()['domains']))"
```
CI gates on `aureon/saas` in the strict tier (`operator-ci.yml`): ruff-clean +
mypy-clean + `tests/test_saas_*.py` green.
---
## Runtime and deployment status
The SaaS implementation is source-visible and testable offline, but runtime and
deployment are on terminal **HOLD**. `deploy/docker-compose.saas.yml` starts only
non-networked protection preflights. The frontend image performs no package
lifecycle or source build, starts no nginx listener, and exposes no port.
Frontend `dev`, `preview`, E2E, trading, and runtime package scripts are held.
Do not inject tenant, Supabase, operator, model, or hosting credentials merely to
verify this source. No console, gateway, manifest route, tenancy service, or
payment route is deployed by the supported entrypoints.
---
## Billing (Phase 6C)
**The authority boundary first: the platform never initiates payments.** Metering
is record-only; the single money-moving route ships disabled and is audited.
Three layers, all reusing the wallet that already existed
(`gas_tank_accounts` + `gas_tank_transactions`, the Β£-denominated performance-fee
tank with its working `gas-tank-topup` edge function):
### 1. Support the project (money in β€” the user's choice, on their clock)
Aureon runs free. The console tracks cumulative runtime locally and, after ~4
hours of use, shows a **small dismissible corner card** β€” never a modal, never a
gate β€” inviting the user to support the project via the SumUp payment links
(`VITE_SUPPORT_PAYMENT_URLS`; defaults are the project's live links). The flow
is **self-confirmed**: the user says what they gave, the existing
`gas-tank-topup` edge function credits their tank, and a `payment_transactions`
row (`provider: sumup`, `status: self_confirmed`) is recorded so payouts can be
reconciled manually against SumUp. Trust-based by design; the admin
`verify-payment` flow remains for disputes.
### 2. Usage metering (record-only β€” `aureon/saas/metering.py`)
When `AUREON_BILLING_METERING=1`, every `/api/*` request becomes one
`api_request` event (tenant-attributed via the Supabase JWT when present) in a
bounded buffer, flushed by a daemon thread to the `saas_usage_events` table via
PostgREST. Per-provider LLM token usage β€” parsed by the operator all along but
previously dropped β€” is swept into `llm_tokens` events and the
`aureon_llm_tokens_total` Prometheus counter. Honesty rules: drop-oldest is
counted, flush failures are counted and dropped, and `GET /api/billing/status`
names the true sink (`disabled` / `prometheus-only` / `supabase`). **Nothing in
this layer debits a balance.** Per-tenant token attribution is staged (fan-out
runs in worker threads; claiming it now would be dishonest).
### 3. Billing API + the fee path's first caller (`aureon/saas/billing.py`)
| Method | Route | Returns |
|--------|-------|---------|
| `GET` | `/api/billing/status` | Always 200: config, metering stats, token totals, flags. |
| `GET` | `/api/billing/balance` | The tenant's gas-tank account (503/401/404/502 β€” precise, never a bare 500). |
| `GET` | `/api/billing/usage` | The tenant's usage events + buffer stats. |
| `POST` | `/api/billing/charge-fee` | ⚠ **MOVES MONEY β€” ships OFF.** 403 unless `AUREON_BILLING_CHARGE_ENABLED=1`. |
`charge-fee` is the first-ever caller for the previously dead
`gas-tank-deduct-fee` edge function (the performance-fee math β€” 20%/10% on
profit above the high-water mark β€” stays there, single source of truth). It
exists for the server-side trade loop: the profit-realization step POSTs
`{user_id, profit, trade_execution_id}` behind the operator bearer, and every
attempt is audited as a `fee_charge` usage event. No live trader is wired to it
yet β€” that hookup is a deliberate, separate decision.
### Billing environment
| Var | Default | Effect |
|---|---|---|
| `SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` | unset | PostgREST + edge-function access |
| `AUREON_BILLING_METERING` | off | usage-event recording/flushing |
| `AUREON_BILLING_FLUSH_S` / `_BUFFER_MAX` / `_HTTP_TIMEOUT_S` | 10 / 5000 / 5 | tuning |
| `AUREON_BILLING_CHARGE_ENABLED` | **off β€” moves money** | enables charge-fee |
| `VITE_SUPPORT_PAYMENT_URLS` | the project's SumUp links | support links |
| `VITE_SUPPORT_PROMPT_HOURS` / `VITE_SUPPORT_SNOOZE_HOURS` | 4 / 12 | prompt cadence |
### Hardening notes (recorded, not yet fixed)
- `gas_tank_accounts.fees_paid_today` is never reset by any job.
- `payment_transactions.currency` defaults to `'EUR'` in the schema while every writer sends `'GBP'`.
- Several older tables use permissive `USING(true)` RLS write policies (service-role reliance); `saas_usage_events` deliberately does not.
- `REFUND` / `ADJUSTMENT` transaction types are declared but never produced.
- `aureon_user_sessions.gas_tank_balance` is vestigial β€” overloaded as "harvested capital", unrelated to the wallet.
- When the operator bearer (`AUREON_OPERATOR_API_KEY`) and the Supabase JWT bridge are both enabled they compete for the same `Authorization` header.
---
## Unified UI Shell (Phase 7)
One professional interface for the entire repo β€” every capability behind one
sidebar, one router, one design language (the Aureon prism identity, kept by
the creator's choice).
**Architecture: the shell wraps the monolith, it does not rewrite it.**
- **`frontend/src/shell/nav.ts`** β€” the single source of truth: sections β†’
routes β†’ icons β†’ lazy page loaders. The sidebar, router, breadcrumbs, and
command palette all derive from this table; adding a surface is one entry.
- **`ShellLayout.tsx`** β€” collapsible grouped sidebar (built on the previously
unused shadcn `ui/sidebar.tsx` + existing `--sidebar-*` tokens), top bar with
breadcrumb, live platform-status dot (polls `GET /api/status`), and a
**⌘K command palette** that jumps to any surface.
- **Routes** (all `React.lazy` β€” every surface is its own chunk):
*Trading* war-room Β· live bridge Β· Orca Β· analytics Β· backtesting β€”
*Research & Planetary* Harmonic Nexus Β· Earth resonance Β· solar Β· NOAA space
weather β€” *Cognition & LLM* **Operator Chat** (new: talks to
`/api/cognition/reason`, renders grounding sources, tool calls, and the
conscience verdict honestly) Β· agent company β€” *Coding System* organism Β·
skills Β· work orders Β· director bridge β€” *Operations* gold & capital Β·
systems integration Β· operational console β€” *Platform* overview (new) Β·
repo map Β· billing & support (new) Β· **legacy console**.
- **The legacy nine-tab console is preserved intact** at `/platform/console`
(exported as `LegacyConsole` from `App.tsx`); its five entangled tabs
(live-ops, trading audits, security, inventory, evidence) still work, and old
`#tab` deep links redirect into the right shell route.
- **Resilience**: every route renders inside a per-route error boundary (a
crashing dashboard never takes down the shell) with a retry, and live-data
surfaces carry an honest notice when no backend is connected.
- **Performance**: `vite` manualChunks + route-level lazy loading split the
former 1,566 kB single bundle into a 227 kB entry + per-surface chunks; the
oversize-chunk warning is gone.
Historical UI builds and smoke tests are implementation evidence only. The
current release image deliberately performs no frontend build and starts no
browser-facing listener.
---
## Staged ledger
The implementation ledger below separates source work from runtime release:
| # | Phase | Item | Done? |
|---|-------|------|-------|
| 6A | Platform layer | Categorized catalog + domain adapters + honest status + gateway routes + Supabase JWT bridge. | β˜‘ |
| 6B | Frontend release | Historical nginx/compose design is retained in source history; active image and package routes are terminal HOLD and build/start nothing. | HOLD |
| 6C | Billing | Support-the-project flow (SumUp, self-confirm β†’ gas tank) + record-only usage metering + billing read API + env-gated charge-fee proxy. Staged: per-unit debits, per-tenant token attribution, automated payment capture (SumUp API/Stripe), trade-loop fee hookup, usage panel UI. | β˜‘ |
| 7 | Unified UI shell | Router + grouped sidebar + command palette over every surface; new Overview/Operator-Chat/Billing pages; legacy console preserved; per-route error boundaries; code-split bundle. Staged: extracting the legacy console's five entangled tabs into shell routes. | β˜‘ |
| 9 | Organism connectome | Source and tests cover sense/touch/weave/pulse and related APIs. Supervisor routes remain terminal HOLD; no dormant organ or HNC daemon is started. | SOURCE ONLY |
| 10 | Investor-ready repo | Professional README (hero + badges + staged registers), [`COMPANY.md`](../COMPANY.md) with real credentials (Companies House NI696693, Silver Innovate NI cert), root health files + templates, de-cluttered root (binaries/junk/launchers/standalone scripts relocated with history preserved). Secret hygiene: removed a tracked `.env1.txt`. | β˜‘ |
| 22 | Cognitive Systems SaaS | The cognitive + meta-cognitive substrate (field Β· bus Β· mycelium Β· connectome Β· brain) as verified read APIs at `/api/cognition*`, each response provenance-stamped (`live`/`real_derived`/`cached_real`/`no_data`, never fabricated); auto-metered + auth-gated; console page at `/cognition/systems`. No cold-boot of heavy organs from a GET. See [`architecture/COGNITIVE_SAAS.md`](architecture/COGNITIVE_SAAS.md). | β˜‘ |
| 39 | Consciousness catalog | The Phase 25-38 consciousness organs categorized into one honest surface β€” self-perception Β· selfhood Β· purpose Β· governance Β· workforce Β· body β€” each with its purpose, route, **safety posture** (`read_only_assess` / `records_only_gated` / `reversible_ascent_gated`) and live truth_status. Registry-as-data at `/api/consciousness`; **Consciousness** page at `/cognition/consciousness`. Dormant organ β†’ `no_data`, never fabricated. See [`architecture/CONSCIOUSNESS_CATALOG.md`](architecture/CONSCIOUSNESS_CATALOG.md). | β˜‘ |
| 41 | Automation progress index | One honest % toward "the whole repo, fully automated", decomposed by dimension (connectivity Β· integration Β· consciousness Β· surfacing, weight-renormalized) + by category, composed from real coverage signals only (clamped; dormant β†’ `index_pct: null` + `no_data`, never inflated). `/api/automation`; headline card on Overview; observational only. See [`architecture/AUTOMATION_INDEX.md`](architecture/AUTOMATION_INDEX.md). | β˜‘ |
| 54 | Feature switchboard | Turn every system feature on/off at human discretion β€” instance-owned, encrypted flag store (`~/.aureon/feature_flags.json.enc`), applied at daemon boot via `bootstrap_credentials()`. Safe toggles + a hard-boundary tier behind a typed-confirm arming ceremony. Flipping only sets the flag's own env var β€” no downstream gate (conscience veto / approval queue / runtime dry-run) is ever removed. `/api/switchboard`; **Switchboard** page at `/platform/switchboard`. See [`architecture/FEATURE_SWITCHBOARD.md`](architecture/FEATURE_SWITCHBOARD.md). | β˜‘ |
---
## Guardrails
The operator security envelope is reused; the Supabase JWT bridge is **off by
default**; hard authority boundaries (live-trade / payment / credential / filing)
and the `AUREON_LLM_OFFLINE` / `AUREON_AUDIT_MODE` guards stay in force. The 715
modules are surfaced and health-checked, **not** rewritten. Status is honest.
`state/` caches and `frontend/public/` manifests are generated and gitignored.
The Supabase project and its secrets are untouched (read-only bridge).
---
## πŸ“š Related
- [`runbooks/PRODUCTION_GRADE.md`](runbooks/PRODUCTION_GRADE.md) Β· [`architecture/AUREON_OPERATOR_SWITCHBOARD.md`](architecture/AUREON_OPERATOR_SWITCHBOARD.md) Β· [`deployment/OPERATOR_DEPLOY.md`](deployment/OPERATOR_DEPLOY.md)
- [`MODULES_AT_A_GLANCE.md`](MODULES_AT_A_GLANCE.md) β€” the 24 filesystem domains
- `aureon/saas/` Β· `.github/workflows/operator-ci.yml`
---
***🌐 The organism, made legible: every system categorized, every domain connected, every status honest.***