All 10 agents enrolled in Cloudron with TOTP 2FA (vp-secops and vp-techcompliance via the --enable-2fa second pass after the setup page fix). Gitea tokens generated and verified for both Q3 VPs. Remaining blockers are Charles-side: Redmine app access grants and a Discourse signup/approval investigation.
138 lines
6.0 KiB
Markdown
138 lines
6.0 KiB
Markdown
# JOURNAL.md — Agent Identity Provisioning
|
|
|
|
> Append-only decision & pattern log. One section per change. Never delete or reorder.
|
|
|
|
---
|
|
|
|
## 2026-08-13 — Session 2: BW sync fix, 2FA, all four systems proven
|
|
|
|
**Commits:** f633a10, be2f607, 2d01a9f, 2258e1b, fcd484f, f414b0b
|
|
|
|
### Decisions
|
|
|
|
1. **BW sync lifecycle**: `BitwardenHelper.login()` must end with `bw sync`.
|
|
Root cause of session-1 "vanishing items": the container's local cache
|
|
was never synced after login. `list_items()` also syncs before reading.
|
|
2. **Container UID/GID**: provision user is 1002:1002, matching the host
|
|
TSGCOO account, so bind-mount state files are owned by the invoking user.
|
|
During rapid iteration `:latest` tagging with overwrite is acceptable.
|
|
3. **Source mounted read-only** into the container (bw_helper.py,
|
|
provision-agent.py, test files) so selector iterations do not require
|
|
image rebuilds.
|
|
4. **One-off scripts kept**: the exploration scripts (dump-cloudron-dom.py,
|
|
enable-cloudron-2fa.py, etc.) remain in the repo as proven references;
|
|
their flows have been consolidated into provision-agent.py.
|
|
|
|
### Patterns (selectors and flows that WORK)
|
|
|
|
**Cloudron panel (Pankow/Vue):**
|
|
- Login: `#inputUsername` / `#inputPassword`, type via `page.keyboard.type()`
|
|
(never `fill()`), submit via `[role="button"]:has-text("Log in")`.
|
|
- 2FA prompt on OIDC login: `#inputTotpToken` + `#totpTokenSubmitButton`
|
|
(NOT `#inputTotp`).
|
|
- 2FA enrollment: `#/profile` -> click `text=Setup` -> click
|
|
`text=switchToTotp` (Cloudron defaults to Passkey) -> secret is base32
|
|
text on the page (regex `[A-Z2-7]{16,}`) -> enter code in
|
|
`#totpTokenInput` -> click Enable.
|
|
|
|
**Gitea (proven session 1):**
|
|
- SSO button: `a[href*="oauth2/cloudron"]` at `/user/login`.
|
|
- Token page `/user/settings/applications`: fill `#name` via JS evaluate,
|
|
scopes are radio buttons, extract 40-hex from `.ui.info.message`.
|
|
|
|
**Discourse:**
|
|
- Login modal via `.login-button`, then `button:has-text("OpenID")`.
|
|
- First SSO lands on `/signup` with email pre-authenticated: fill
|
|
`#new-account-username`, click Sign Up.
|
|
- User API key: RSA keypair -> `/user-api-key/new?...&public_key=<PEM>` ->
|
|
click Authorize -> capture POST response -> decrypt with **PKCS1v15**
|
|
(not OAEP) -> payload JSON `{"key": "..."}`.
|
|
- API auth header is `User-Api-Key` (admin keys use `Api-Key`).
|
|
|
|
**Redmine:**
|
|
- SSO button: `#login-oauth-submit-1` ("Continue with KNEL Cloud").
|
|
- Prereq: Cloudron admin must grant the user access to the Redmine app,
|
|
otherwise OIDC shows "You do not have access" and redirects back.
|
|
- API key: `/my/account` -> click Show in `.api-key-actions` -> read
|
|
`#api-access-key` (40-hex). If absent, click the Reset link found by
|
|
DOM traversal from `#api-access-key` (generic `a:has-text("Reset")`
|
|
clicks the wrong section and logs you out).
|
|
|
|
### Username derivation
|
|
|
|
Manifest `name` is hyphenated (vp-techops); app usernames are not
|
|
(vptechops). Default: `agent.get("username", name.replace("-", ""))`.
|
|
Override with an explicit `username:` field in agents.yaml.
|
|
|
|
### Gotchas
|
|
|
|
- Em dashes (U+2014) break Python source; use `--`.
|
|
- Python f-string interpolation inside JS template literals does not work;
|
|
build JS strings with plain concatenation inside evaluate().
|
|
- Discourse admin API keys page is admin-only; User API keys are the
|
|
self-service path.
|
|
- Gitea token page needs `wait_until="domcontentloaded"` (networkidle
|
|
times out).
|
|
|
|
### Verification results (vp-techops)
|
|
|
|
| System | Credential | Verified via |
|
|
|---|---|---|
|
|
| Cloudron | password + TOTP | full login round-trip |
|
|
| Gitea | 40-char token | `GET /api/v1/user` -> vptechops |
|
|
| Discourse | 32-char user key | `GET /latest.json` with User-Api-Key |
|
|
| Redmine | 40-char API key | `GET /users/current.json` -> id 11 |
|
|
|
|
---
|
|
|
|
## 2026-08-14 — Session 3: 8 agents enrolled, repo transfer, KNEL org layout
|
|
|
|
**Commits:** a96990d (org-buildout), 657939d/e3e5451 (KNELSecretsManager),
|
|
c564531 (TSYSGroupAIOS), 87298df (aip)
|
|
|
|
### Decisions
|
|
|
|
1. **Repo ownership split**: org-buildout is docs-only (BW CLI source
|
|
moved out); BW CLI lives in KNEL/KNELSecretsManager with ADR-002;
|
|
agent-identity-provisioning transferred TSYSGroupCorporate -> KNEL,
|
|
owned by VP TechOps. All 31 KNEL repos cloned to ~/projects/KNEL/.
|
|
2. **Gitea links commits to accounts by EMAIL.** Registries must use
|
|
Cloudron-issued addresses (tsgstaff-coo-*@turnsys.com), not synthetic
|
|
ones. Fixed in clone-as.sh/agent-profile.sh.
|
|
3. **tea credential helper** installed globally (vptechops token) --
|
|
clones over HTTPS work with clean URLs. NOTE: all pushes from this
|
|
host to git.knownelement.com now authenticate as vptechops; watch
|
|
attribution when pushing from TSGCOO-owned repos.
|
|
|
|
### Patterns proven this session
|
|
|
|
**Invite acceptance (setupaccount.html):** fields #inputDisplayName,
|
|
#inputPassword, #inputPasswordRepeat (username prefilled). Pankow forms
|
|
require click + keyboard.type. Submit is <div role="button"> "Set up",
|
|
disabled until form valid.
|
|
|
|
**2FA on fresh accounts:** after acceptance the SPA sits on
|
|
setupaccount.html; hash navigation cannot leave it -- goto panel root
|
|
first. Forced-2FA screen says "Set up passkey" (profile says "Setup")
|
|
-- match both. TOTP flow identical to session 2.
|
|
|
|
### Results (10 identities)
|
|
|
|
| Agent | Cloudron | 2FA | Gitea | Discourse | Redmine |
|
|
|---|---|---|---|---|---|
|
|
| vp-techops | yes | yes | token | key | key |
|
|
| vp-secops | yes | 2nd pass | BLOCKED (no app access) | acct created, retry | BLOCKED |
|
|
| vp-techcompliance | yes | 2nd pass | BLOCKED (no app access) | acct created, retry | BLOCKED |
|
|
| coo/svp-knel/svp-tctc | yes | yes | - (Q4 phase1) | - | - |
|
|
| vp-investing/vp-trading | yes | yes | - | - | - |
|
|
| vp-compliance | yes | yes | - | - | - |
|
|
| vp-facilities | cleanup pass | cleanup pass | - | - | - |
|
|
|
|
### Open items for Charles
|
|
|
|
- Grant Gitea/Redmine (and Discourse approval if required) app access
|
|
for vp-secops and vp-techcompliance -- Gitea OIDC returns
|
|
"The authorization request was denied." until granted (same class as
|
|
the session-2 Redmine blocker).
|
|
- vp-techops stale Gitea tokens cleanup still pending.
|