skills: cloudron.md records pointer

This commit is contained in:
TSYS Group COO
2026-09-08 06:03:06 -05:00
parent 814188e582
commit 1a1a7ca581
6 changed files with 230 additions and 81 deletions
+81 -20
View File
@@ -60,8 +60,10 @@ scripts/cloudron-api.sh GET /users/<uid> # fetch one user object
Route notes (this Cloudron, 2026-09): users are addressed by `uid-…` UUIDs,
not usernames; there is NO `/api/v1/login` (dashboard auth is its own OIDC
flow); there is NO admin route to reset another user's 2FA — so the seed
capture in step 2 is not optional.
flow, client `cid-webadmin`, PKCE S256); admin CAN clear a user's TOTP via
`POST /api/v1/users/<uid>/totp_disable` (empty JSON body) — use it when a
seed was lost, then re-enroll with capture. Admin password set:
`POST /api/v1/users/<uid>/password {"password":"…"}`.
## Procedure
@@ -76,26 +78,61 @@ capture in step 2 is not optional.
Verify the account exists and matches the identity email pattern
(`tsgstaff-<login>@turnsys.com` for COO-line identities). Generate the unique
password (32 chars), set it, and confirm a login works (step 2 proves it).
password (32 chars), set it via the admin API, and store it in the vault item
login password field:
```bash
scripts/cloudron-api.sh POST /users/<uid>/password '{"password":"<32-char>"}'
sm setfield "<login> Cloudron" password "<32-char>"
```
### 2. TOTP 2FA — enroll and CAPTURE THE SEED
Enroll TOTP via the profile self-service surface and write the base32 seed to
the vault item before anything else:
Two cases:
```bash
sm setfield "<login> Cloudron" totp_seed "<BASE32SEED>"
```
- **Seed lost / never captured** (TOTP enforced, no `totp_seed` in vault):
clear first — `scripts/cloudron-api.sh POST /users/<uid>/totp_disable`
then proceed to enroll fresh.
- **No 2FA yet**: enroll directly.
Enrollment runs in the identity's own dashboard session (the totp routes are
self-scoped — an admin token gets 404s there):
1. `scripts/cloudron-oidc-login.sh <username> <vault-item>` — full OIDC
login (PKCE + interaction + consent) yielding the identity's Bearer at
`/tmp/.oidc-bearer-<username>`.
2. `scripts/cloudron-totp-enroll.sh <username> <vault-item>` — reads the
pending secret, enables with a computed code, stores the seed via
`sm setfield`, and verifies end-to-end.
If the API confirm path is rejected, fall back to the dashboard UI in the
browser: the forced "Enable TOTP" dialog shows the seed; read it, compute
with `scripts/totp.sh`, fill the Token field and submit. CRITICAL: the
pending secret REGENERATES every time the dialog re-renders — always read
the seed and submit the code within the same window, and verify enrollment
by a real TOTP login (a totpToken in a login is silently ignored for users
without 2FA — "success" there proves nothing).
If the identity already has 2FA enforced and no seed exists anywhere: STOP and
ask the founder. There is no programmatic recovery (verified 2026-09-08).
Compute codes offline when needed: `scripts/totp.sh <seed>`.
### 3. SSO first-login into each system
### 3. TOTP enrollment (in the identity's dashboard session)
```bash
scripts/cloudron-oidc-login.sh <username> <vault-item> # OIDC login -> Bearer
scripts/cloudron-totp-enroll.sh <username> <vault-item> # enroll + seed + verify
```
If the API enable path is rejected, fall back to the dashboard UI dialog (see
step 2's note). The verify pass inside the scripts performs a real TOTP login
and is the ONLY trustworthy success signal.
### 4. SSO first-login into each system
The interactive login is scriptable with a cookie jar — no browser needed.
`scripts/oidc-login.sh <system>` runs the whole flow and leaves a session
cookie jar at `/tmp/oidc-<login>-<system>.txt`. Entry points:
`scripts/oidc-login.sh <system> <login>` runs the whole flow and leaves a
session cookie jar at `/tmp/oidc-<login>-<system>.txt`. Entry points:
| System | SSO start point | After login |
|---|---|---|
@@ -106,10 +143,11 @@ cookie jar at `/tmp/oidc-<login>-<system>.txt`. Entry points:
Key gotcha (cost a debugging session — do not rediscover): the OIDC
interaction login is `POST /openid/interaction/<uid>/login` with JSON body
`{"username": "...", "password": "..."}` — the field is **`username`**, not
`login`, and the response is `{"redirectTo": ...}` on success.
`{"username": "...", "password": "...", "totpToken": "..."}` — the field is
**`username`**, not `login`, and the response is `{"redirectTo": ...}` on
success.
### 4. Mint per-system API keys (as the identity, via its session)
### 5. Mint per-system API keys (as the identity, via its session)
- **Redmine**: My account → API key (visible to self); also returned by
`GET /users/current.json` with the session.
@@ -123,9 +161,12 @@ interaction login is `POST /openid/interaction/<uid>/login` with JSON body
Store each key in the vault item immediately (schema below).
### 5. Vault item schema — `creds/<login>`
### 6. Vault item schema — `creds/<login>`
One item per identity (NOT per system). Create it as a LOGIN item with:
One item per identity (NOT per system), created as a LOGIN item (never a
note — `sm set` makes notes; `sm convert <name>` converts, but REGENERATE
the password via the admin API afterwards because convert drops field
values):
- login username = Cloudron username; password = the unique Cloudron password
- URI = `https://my.knownelement.com`
@@ -137,7 +178,20 @@ Historical items that predate this schema (e.g. `sec-*`, `sre-*` shared-
password items) are remediation targets under #942 — upgrade them when the
owner lane assigns the work, do not replicate their shape.
### 6. Verify the trail
### 7. Update the org chart (MANDATORY — an uncharted identity is an incomplete task)
After provisioning changes the identity estate:
1. Update the org table in `TSYSGroupCorporate/TSYSGroupAIOS`
`agents/global/AGENTS.md` (branch + PR per the house-rules flow; the
symlink serves it live once merged).
2. Update `reachableceo/org-buildout` `docs/org-chart.md` (the rendered
chart) in the same turn.
3. Note the change on the provisioning ticket.
Details: `references/org-chart-update.md`.
### 7. Verify the trail
Prove each system knows the identity (expected: 200 + the identity's own
name; a 200 from the wrong identity is a FAIL):
@@ -155,12 +209,19 @@ the workspace STATUS/memory.
- `references/cloudron.md` — admin API routes, OIDC interaction flow, 2FA.
- `references/systems.md` — per-system SSO/API details and known breakage.
- `references/vault.md` — sm CLI entry points, gotchas, fallbacks.
- `references/org-chart-update.md` — the mandatory post-provisioning chart sync.
- `references/incident-log.md` — what went wrong on 2026-09-07/08 and why, so
the same failures are not re-derived.
## Scripts
- `scripts/cloudron-api.sh METHOD PATH [JSON]` — authenticated admin call.
- `scripts/oidc-login.sh <glpi|gitea|discourse|redmine> <login>` — full SSO
login via cookie jar; reads the identity's creds from the vault.
- `scripts/totp.sh <base32-seed>` — current 6-digit TOTP code (pure bash).
- `scripts/cloudron-oidc-login.sh <username> [vault-item]` — full OIDC
dashboard login (PKCE + interaction + consent); Bearer at
`/tmp/.oidc-bearer-<username>`.
- `scripts/cloudron-totp-enroll.sh <username> [vault-item]` — enroll TOTP,
capture the seed to the vault, verify end-to-end.
- `scripts/oidc-login.sh <glpi|gitea|discourse|redmine> <login>` — system SSO
login via cookie jar.
- `scripts/totp.sh <base32-seed>` — current 6-digit TOTP code (pure bash,
RFC-7636-vector tested).