release: v1.47.0 - Subtype Management & Metrics Drivers Engine, Explicit Secret Inheritance & Cross-Platform Agents

This commit is contained in:
2026-08-08 15:39:16 -04:00
parent 603156cbfe
commit 8bfde63684
8 changed files with 60 additions and 64 deletions
+5 -4
View File
@@ -90,10 +90,11 @@ cp setup.env.example setup.env # then edit setup.env: set CFG_DOMAIN to your
./setup.sh
```
You need **Docker** + **Docker Compose**. `./setup.sh` is idempotent — re-run
any time to converge the stack to `./config/`. For the full config reference,
architecture, see the
**[GitHub repository](https://github.com/theta42/theta-suite)**.
You need a **Domain**, some **Ports Forwarded**, **Docker** +
**Docker Compose**. `./setup.sh` is idempotent — re-run any time to converge the
stack to `./config/`. For the full config reference, architecture, see the
**[GitHub repository](https://github.com/theta42/theta-suite#before-you-begin)**
for a details.
## Related projects
+27 -51
View File
@@ -8,48 +8,9 @@ description: theta-suite's central secrets architecture — OpenBao as the singl
theta-suite keeps **every secret in one place: [OpenBao](https://openbao.org/)**
(a Vault-community fork), running on the `theta-net` docker network at
`http://openbao:8200`. The three apps (SSO Manager, proxy, jump host) load
their boot secrets from it; end users get personal per-user secret storage
through the SSO UI; and external apps get scoped, self-contained access to
their own namespace.
This page is the operator reference. For the package API, see
[@simpleworkjs/bao-conf](https://simpleworkjs.github.io/bao-conf/).
## Why a central store
Before this, secret handling was partial and inconsistent: only the SSO read
one path from OpenBao; the proxy and jump host read bind-mounted
`./config/*-secrets.js` files; the bootstrap wrote generated OAuth creds to
those files on disk; and the SSO `/api/vault` UI was an ungated, broken
pass-through. Centralising on OpenBao gives every app the same fail-soft load
path, makes per-user secret storage possible, and lets external apps get
least-privilege access without anyone handing them the root token.
## The load path (every app)
1. `@simpleworkjs/conf` **synchronously** loads the bind-mounted
`./config/<app>-secrets.js` at require time — the file is the operator-edit
layer and the fail-soft fallback.
2. `@simpleworkjs/bao-conf`'s `init({ path: '<app>', conf })` **deep-merges**
`secret/data/<app>/conf` from OpenBao over the live `conf` object. It is
**fail-soft**: if OpenBao is unreachable or the path is absent, boot
continues with the file-loaded config.
3. A few secrets are **captured at require time** (notably the OIDC
`clientSecret`, consumed inside `createOidcClient` during
`require('../models')`). So `init()` must resolve *before* that
`require()`. Each app's `bin/www` handles this:
- **proxy** — defers `require('../app')` (which transitively loads models)
behind `bao-conf.init()`.
- **jump host** — gates the explicit `require('../models')` behind
`bao-conf.init()`.
- **SSO** — swaps the old `conf_manager.init()` call (same position in its
existing `.then()` boot chain) for `bao-conf.init()`; nothing in the SSO
captures a secret at require time, so no reordering was needed.
`VAULT_TOKEN` (a scoped per-app token, **not** the root token) and
`VAULT_ADDR=http://openbao:8200` are passed to each container via
`docker-compose.yml`. The `./config/*-secrets.js` mounts stay as the fallback.
`http://openbao:8200`. The SSO Manager acts as management and abstraction point
for secrets management. Via the directory, secrets can be set, cycled, revoked
or inherited. **You are not meant to interact with opanBoa directly.**
## Policies, token role, and tokens
@@ -72,22 +33,37 @@ never passed to a service container.
Directory resources (Services, Hosts, Containers, Sites) manage their application secrets at `secret/data/resources/<resource_slug>/conf` in OpenBao KV-v2.
### 1. Zero-View Security Model
* **API Metadata Only**: `GET /api/directory-admin/resources/:id/secrets` returns key names and metadata (`hasValue: true`, `isInherited: true`, `parentSlug`), but **NEVER returns raw secret values**.
* **Browser Isolation**: Secret values are never exposed in HTML DOM templates, JSON admin APIs, or browser dev tools.
* **Agent-Exclusive Delivery**: Raw secret values are fetched exclusively over TLS by authenticated `theta-agent` instances using machine authorization tokens (`POST /api/v1/agent/secrets`).
* **API Metadata Only**: `GET /api/directory-admin/resources/:id/secrets` returns
key names and metadata (`hasValue: true`, `isInherited: true`, `parentSlug`),
but **NEVER returns raw secret values**.
* **Browser Isolation**: Secret values are never exposed in HTML DOM templates,
JSON admin APIs, or browser dev tools.
* **Agent-Exclusive Delivery**: Raw secret values are fetched exclusively over
TLS by authenticated `theta-agent` instances using machine authorization tokens
(`POST /api/v1/agent/secrets`).
### 2. Multi-Level Hierarchy Secret Inheritance
Resources inherit secrets across any level of the directory hierarchy (`Services / Apps → Hosts / Nodes → Global Sites`):
* An inherited secret reference is stored as `INHERIT:<parent_slug>:<parent_key>` (or `INHERIT:<key>`).
* When requested by `theta-agent`, SSO Manager resolves the inheritance chain dynamically, fetching the final secret value from the parent Site or Host's OpenBao store.
Resources inherit secrets across any level of the directory hierarchy
(`Services / Apps → Hosts / Nodes → Global Sites`):
* An inherited secret reference is stored as `INHERIT:<parent_slug>:<parent_key>`
(or `INHERIT:<key>`).
* When requested by `theta-agent`, SSO Manager resolves the inheritance chain
dynamically, fetching the final secret value from the parent Site or Host's
OpenBao store.
### 3. Key Validation & Generator
* **Key Format**: Secret keys are strictly validated against `^[A-Za-z0-9_]+$` (Standard Environment Variable format, e.g. `DB_PASSWORD`).
* **Cryptographic Generator**: The UI includes a client-side cryptographic secret generator (`window.crypto.getRandomValues`) with length choices from 8 to 128 characters. Populating the input field displays an inline security warning notifying operators to save immediately before values are hidden.
* **Key Format**: Secret keys are strictly validated against `^[A-Za-z0-9_]+$`
(Standard Environment Variable format, e.g. `DB_PASSWORD`).
* **Cryptographic Generator**: The UI includes a client-side cryptographic
secret generator (`window.crypto.getRandomValues`) with length choices from 8 to
128 characters. Populating the input field displays an inline security warning
notifying operators to save immediately before values are hidden.
## On-Demand CLI Secret Delivery (`theta-agent get-secret`)
`theta-agent` delivers secrets on demand directly to local processes, shell scripts, Systemd services, and Docker containers without writing plaintext secret files to disk.
`theta-agent` delivers secrets on demand directly to local processes, shell
scripts, Systemd services, and Docker containers without writing plaintext
secret files to disk.
```bash
# Fetch single raw secret value (stdout, no trailing newline):