docs: session 3 handoff -- all flows proven, loop remaining agents
Session 2 outcome: BW sync fixed, Cloudron 2FA on, all four systems provisioned for vp-techops with verified API keys. Flows consolidated into provision-agent.py and documented in the provisioner JOURNAL.md. Session 3 job: populate agents.yaml invites (plus Cloudron app-access grants) and run the manifest loop for the remaining five agents.
This commit is contained in:
+68
-211
@@ -1,234 +1,91 @@
|
|||||||
# Resume Prompt — Agent Identity Provisioning (Session 2)
|
# Resume Prompt — Agent Identity Provisioning (Session 3)
|
||||||
|
|
||||||
> **Read this entire file before doing anything.** This is a handoff from session 1 which got bogged down in failed Playwright selector iterations. Start fresh with a methodical approach.
|
> **Read this entire file before doing anything.** Session 2 completed all
|
||||||
|
> four systems for vp-techops and consolidated the flows. This session loops
|
||||||
|
> the remaining agents through the manifest.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Who You Are
|
## Who You Are
|
||||||
|
|
||||||
You are the TSGCOO Crush agent (AI assistant for TSYS Group's COO). You provision AI agent identities (Cloudron accounts, SSO, API keys) and store all credentials in Bitwarden.
|
You are the TSGCOO Crush agent (AI assistant for TSYS Group's COO). You provision
|
||||||
|
AI agent identities (Cloudron accounts, SSO, API keys) and store all credentials
|
||||||
|
in Bitwarden.
|
||||||
|
|
||||||
## Governance Rules (from TSYSGroupAIOS/BASELINE-PROMPT.md)
|
## Read First
|
||||||
|
|
||||||
Read `/home/TSGCOO/projects/TSYSGroupAIOS/BASELINE-PROMPT.md` and `/home/TSGCOO/projects/TSYSGroupAIOS/AGENTS.md` before starting.
|
1. `/home/TSGCOO/projects/TSYSGroupAIOS/BASELINE-PROMPT.md` (14 principles)
|
||||||
|
2. `/home/TSGCOO/projects/agent-identity-provisioning/STATUS.md`
|
||||||
|
3. `/home/TSGCOO/projects/agent-identity-provisioning/docs/JOURNAL.md` — all
|
||||||
|
working selectors and flows are documented here
|
||||||
|
|
||||||
Key rules:
|
## State at Session 3 Start (2026-08-13 end of session 2)
|
||||||
- **Docker for everything** — no language toolchains on the host. The ONLY exception: the `bw` wrapper at `~/.local/bin/bw` which invokes Docker.
|
|
||||||
- **No secrets on disk except BW access info** — everything in `~/.config/bw/env` (single-quoted values, perms 600).
|
|
||||||
- **Atomic commits, conventional style** — `feat:`, `fix:`, `docs:` etc. Commit often.
|
|
||||||
- **Develop on master** — push when work is complete.
|
|
||||||
- **30s timeouts for reads, 120s standard, 300s deployments.**
|
|
||||||
- **Container naming**: always `tsys-` prefix. Pin all images (no `:latest`).
|
|
||||||
- **Status.md and JOURNAL.md** must be maintained.
|
|
||||||
- **Redmine** is the system of record for work tracking (exempted during bootstrap phase).
|
|
||||||
- **The `todos` tool is OK** for tactical work within a single task. Redmine is for durable cross-agent tracking.
|
|
||||||
- **Never delete credentials.** The `BitwardenHelper` class has no `delete_item` method by design. Manual deletion via host `bw` CLI is allowed for human-directed operations only.
|
|
||||||
|
|
||||||
---
|
**All flows are proven end-to-end for vp-techops and consolidated into
|
||||||
|
`provision-agent.py`:**
|
||||||
|
|
||||||
## What's Already Built and Working
|
| System | Flow | Status |
|
||||||
|
|
||||||
### Bitwarden CLI Infrastructure (SOLID — do not rewrite)
|
|
||||||
|
|
||||||
- **Host wrapper**: `~/.local/bin/bw` — invokes Docker container with full auth lifecycle
|
|
||||||
- **Container image**: `reachableceo-bw-native:2026.7.0` — debian-slim + pre-compiled bw binary
|
|
||||||
- **Dockerfile**: `/home/TSGCOO/org-buildout/docker/bw-native/Dockerfile`
|
|
||||||
- **Scripts**: `/home/TSGCOO/org-buildout/scripts/bw-cli.sh`, `bw-entrypoint.sh`, `bw-install.sh`
|
|
||||||
- **All scripts pass shellcheck** (zero warnings including info-level)
|
|
||||||
- **BW server**: `https://pwvault.turnsys.com` (self-hosted Vaultwarden)
|
|
||||||
- **BW account**: `coo@turnsys.com`
|
|
||||||
|
|
||||||
The host `bw` wrapper works reliably. Test it: `bw status`, `bw list items`, `bw generate -ulns --length 24`.
|
|
||||||
|
|
||||||
**IMPORTANT**: The "native Rust binary" from Bitwarden's GitHub releases is actually a Node.js SEA (Single Executable Application) — it's Node.js packaged as a single binary. It still prints Node.js errors on crash. The user wanted zero Node.js for CMMC/ITAR compliance. This is a **known issue** that needs to be discussed with Charles. The BW MCP server container (`kneldevstack-aimiddleware-bitwarden-mcp-crush`) is also available but needs wiring into Crush config.
|
|
||||||
|
|
||||||
### Bitwarden Credential File
|
|
||||||
|
|
||||||
`~/.config/bw/env` — all values MUST be single-quoted (password contains `$` chars):
|
|
||||||
```
|
|
||||||
BW_CLIENTID='user.98954ef9-da68-42f2-bcf8-c2343202ea60'
|
|
||||||
BW_CLIENTSECRET='WCOTwi9TbLDwzFwKXiD8pcEQy1PY3r'
|
|
||||||
BW_PASSWORD='yZ6Qw$5h$vdPL9DxEXwxCO#^m0J%dS8@'
|
|
||||||
BW_TOTP_SECRET='VLEZH74VBECT2XLHWMSTS6R4QQ2SBXCJ'
|
|
||||||
BW_SERVER='https://pwvault.turnsys.com'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Repos
|
|
||||||
|
|
||||||
| Repo | Path | Status |
|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `org-buildout` | `/home/TSGCOO/org-buildout` | BW infra committed, AGPLv3, can't push (no Gitea auth) |
|
| Cloudron | invite -> password -> TOTP 2FA | ✅ proven |
|
||||||
| `agent-identity-provisioning` | `/home/TSGCOO/projects/agent-identity-provisioning` | Provisioner code, multiple commits, can't push |
|
| Gitea | SSO -> token | ✅ proven |
|
||||||
| `TSYSGroupAIOS` | `/home/TSGCOO/projects/TSYSGroupAIOS` | Governance framework, clean (BW stuff was moved to org-buildout) |
|
| Discourse | SSO -> signup -> RSA User API key | ✅ proven |
|
||||||
|
| Redmine | SSO -> API key | ✅ proven |
|
||||||
|
|
||||||
---
|
BW vault (4 items): vp-techops Cloudron (TOTP), Gitea, Discourse, Redmine.
|
||||||
|
All keys verified via their APIs.
|
||||||
|
|
||||||
## Current BW Vault State
|
**Infrastructure facts:**
|
||||||
|
- Provisioner: `docker compose run --rm provision` in
|
||||||
|
`/home/TSGCOO/projects/agent-identity-provisioning`
|
||||||
|
- Source is bind-mounted read-only — no rebuild needed for .py changes
|
||||||
|
- Container user matches host TSGCOO (1002:1002); `:latest` tag reuse is
|
||||||
|
fine during iteration
|
||||||
|
- `bw_helper.py` syncs after login and before list — the session-1 state
|
||||||
|
divergence bug is fixed and covered by `test_bw_persistence.py`
|
||||||
|
- Host `bw` wrapper: `export PATH="$HOME/.local/bin:$PATH"` (not in
|
||||||
|
default PATH)
|
||||||
|
|
||||||
|
## What This Session Does
|
||||||
|
|
||||||
|
Loop the remaining agents in `agents.yaml`:
|
||||||
|
|
||||||
|
1. Charles generates Cloudron invites for: vp-secops, vp-techcompliance
|
||||||
|
(Q3), coo, svp-knel, svp-tctc (Q4, phase1-only)
|
||||||
|
2. Fill `cloudron_invite:` URLs in `agents.yaml` (it is gitignored)
|
||||||
|
3. **Charles must grant each user app access in Cloudron** (Redmine lesson:
|
||||||
|
OIDC shows "You do not have access" until the admin grants the app)
|
||||||
|
4. Run per agent:
|
||||||
```
|
```
|
||||||
Items: 2
|
docker compose run --rm provision provision-agent.py --agent vp-secops
|
||||||
30b5c3d7-... - vp-techops Cloudron - totp=no (2FA NOT enabled)
|
|
||||||
b8de991b-... - vp-techops Gitea - totp=no
|
|
||||||
```
|
```
|
||||||
|
Q4 agents: add `--phase1-only`
|
||||||
|
5. Idempotency: BW items short-circuit (`X already exists -- skipping`),
|
||||||
|
so re-runs are safe
|
||||||
|
|
||||||
The vp-techops Cloudron account exists with a stored password. **2FA is NOT enabled** — this is a hard blocker for CMMC compliance.
|
## Prereq Checklist Before Each Agent
|
||||||
|
|
||||||
The vp-techops Gitea token was manually stored during session 1. The token `c0388aa0504bef4e4effdb47c910ef8f6252f5bd` was verified working via Gitea API (`user=vptechops`).
|
- [ ] Invite URL in agents.yaml
|
||||||
|
- [ ] Cloudron app access granted (Gitea/Redmine/Discourse per manifest)
|
||||||
|
- [ ] BW vault has no stale items for that agent
|
||||||
|
|
||||||
---
|
## Known Issues (do not re-litigate)
|
||||||
|
|
||||||
## Current Provisioner State
|
1. **bw "native" binary is a Node.js SEA** — prints Node errors on crash.
|
||||||
|
CMMC/ITAR zero-Node goal is not actually met. Needs a Charles decision.
|
||||||
|
2. **Gitea stale tokens** from session 1 may exist for vp-techops — cleanup
|
||||||
|
pending (revoke all but the stored one).
|
||||||
|
3. **Repos can't be pushed** — TSGCOO has no Gitea auth yet. All commits
|
||||||
|
are local. Wire TSGCOO's Gitea credentials when available.
|
||||||
|
4. **Discourse User API keys are user-scoped** — admin operations still
|
||||||
|
need an admin key (assign to vp-techops per STATUS.md inbox).
|
||||||
|
|
||||||
The provisioner container runs as non-root user `provision` (UID 997). It has a known **BW state sync issue** — the `bw-state/` bind mount gets out of sync between container runs. This MUST be fixed before any Playwright work matters.
|
## Mistakes to Avoid (carried forward)
|
||||||
|
|
||||||
### Key Files
|
- Never delete BW items to fix duplicates — `update_item()` in place
|
||||||
|
- One comprehensive DOM dump per page before writing selectors
|
||||||
- `provision-agent.py` — main Playwright automation (~850 lines, lots of debug dumps)
|
(see `_debug_dump` and JOURNAL.md patterns)
|
||||||
- `bw_helper.py` — BW CLI wrapper with safety guarantees (no delete, duplicate prevention, update in place)
|
- `page.keyboard.type()` not `fill()` for Vue/Pankow forms
|
||||||
- `agents.yaml` — manifest with real vp-techops invite link
|
- `[role="button"]` not `button` for Cloudron UI
|
||||||
- `Dockerfile` — Playwright base + native bw binary + python3-pip + non-root user
|
- Em dashes break Python source — use `--`
|
||||||
- `docker-compose.yml` — env_file for BW creds, bind mounts for state
|
- Redmine Reset: use the DOM-traversal targeting in `provision_redmine`,
|
||||||
- `test_bw_helper.py` — 10 credential lifecycle tests (tests 1-4 verified passing)
|
never a generic `a:has-text("Reset")` match
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The SSO Pattern (Proven for Gitea)
|
|
||||||
|
|
||||||
All Cloudron-managed apps use the same SSO pattern:
|
|
||||||
|
|
||||||
1. **Login to Cloudron panel first** (`cloudron_panel_login()`) to establish OIDC session
|
|
||||||
2. **Navigate to app login page**, click SSO/OAuth button
|
|
||||||
3. **Cloudron OIDC handles auth** — may show login form (if session expired) or consent page
|
|
||||||
4. **Redirect back to app** authenticated
|
|
||||||
|
|
||||||
### Cloudron UI Technical Details
|
|
||||||
|
|
||||||
- Cloudron uses **Pankow/Vue** components
|
|
||||||
- Login form: `#inputUsername`, `#inputPassword`, `#inputTotp` (for 2FA)
|
|
||||||
- Submit button is `<div role="button">Log in</div>` — NOT a `<button>` tag
|
|
||||||
- `page.fill()` does NOT work with Vue reactive forms — must use `page.click()` + `page.keyboard.type()`
|
|
||||||
- Button click: `[role="button"]:has-text("Log in")` works; `button:has-text("Log in")` does NOT
|
|
||||||
- Profile/2FA page is at `#/profile` (NOT `#/security` which is admin-only)
|
|
||||||
- After login, Cloudron redirects to `#/apps` — use `window.location.hash = "#/profile"` to navigate
|
|
||||||
|
|
||||||
### App-Specific SSO Buttons
|
|
||||||
|
|
||||||
| App | Login URL | SSO Button Selector |
|
|
||||||
|---|---|---|
|
|
||||||
| **Gitea** | `https://git.knownelement.com/user/login` | `a[href*="oauth2/cloudron"]` (text: "Sign in with cloudron") |
|
|
||||||
| **Redmine** | `https://projects.knownelement.com/login` | `button#login-oauth-submit-1` (text: "Continue with KNEL Cloud") |
|
|
||||||
| **Discourse** | `https://community.turnsys.com/` | Click "Log In" button to open modal, then SSO button inside modal (says "with OpenID Connect" — selector unknown, needs DOM dump) |
|
|
||||||
|
|
||||||
### Gitea Token Page (`/user/settings/applications`)
|
|
||||||
|
|
||||||
- Token name input: `#name` (may not be visible to Playwright — use JS `evaluate` to fill)
|
|
||||||
- Scopes are **radio buttons** per category (not checkboxes): `write:repository`, `write:user`, `write:organization`, `write:issue`, `write:package`, `read:notification`, `read:misc`
|
|
||||||
- Generate button: `button:has-text("Generate Token")`
|
|
||||||
- Token appears in `.ui.info.message` as plain text (40-char hex string) — extract with regex `[a-f0-9]{40}`
|
|
||||||
- Use `wait_until="domcontentloaded"` (NOT `networkidle` — times out)
|
|
||||||
|
|
||||||
### Redmine API Key Page (`/my/account`)
|
|
||||||
|
|
||||||
- Needs DOM dump after successful SSO login
|
|
||||||
- Previous session never got past the Redmine login page — OIDC consent completes but redirects back to `/login`
|
|
||||||
- This may be a Redmine-side config issue (user not auto-creating from SSO)
|
|
||||||
|
|
||||||
### Discourse API Key
|
|
||||||
|
|
||||||
- Discourse API keys typically require admin to create
|
|
||||||
- Non-admin users likely cannot self-generate
|
|
||||||
- Charles may need to pre-create these
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What Needs to Happen (In Order)
|
|
||||||
|
|
||||||
### Phase 0: BW Lifecycle Solid (DO THIS FIRST)
|
|
||||||
|
|
||||||
The BW state management in the provisioner container is broken. Symptoms:
|
|
||||||
- Items appear in one container run, vanish in the next
|
|
||||||
- Host `bw` and container `bw` show different vault states
|
|
||||||
- `bw sync` is called but doesn't seem to propagate
|
|
||||||
|
|
||||||
**Fix approach:**
|
|
||||||
1. Understand why the `bw-state/` bind mount doesn't persist login state across runs
|
|
||||||
2. Ensure `bw sync` is called after every login and before every read
|
|
||||||
3. Write a test that creates a dummy item, reads it back from a separate container run, verifies it persists
|
|
||||||
4. Only proceed when you can reliably: create item → read it from host CLI → read it from container → update it → verify update persists
|
|
||||||
|
|
||||||
### Phase 1: Cloudron 2FA
|
|
||||||
|
|
||||||
The TOTP secret extraction works but the confirmation step fails. The Cloudron profile page at `#/profile` has:
|
|
||||||
- A `<label>TOTP</label>` section
|
|
||||||
- A `#totpTokenInput` for entering the confirmation code
|
|
||||||
- An enable button (selector unknown — needs DOM dump of the profile page when logged in)
|
|
||||||
|
|
||||||
**The `--enable-2fa` flag exists** in provision-agent.py but the button selectors are wrong. Need to:
|
|
||||||
1. Login to Cloudron panel (keyboard.type + role=button click — this works)
|
|
||||||
2. Navigate to `#/profile`
|
|
||||||
3. DOM dump the page to find the real TOTP enable button
|
|
||||||
4. Click it, extract secret, enter TOTP code, confirm
|
|
||||||
5. Verify 2FA works by attempting a fresh login that prompts for TOTP
|
|
||||||
|
|
||||||
### Phase 2: SSO Login for All Apps
|
|
||||||
|
|
||||||
Using the established pattern:
|
|
||||||
1. `cloudron_panel_login()` first
|
|
||||||
2. Per-app SSO button click
|
|
||||||
3. OIDC handling
|
|
||||||
|
|
||||||
Gitea SSO is proven working. Redmine and Discourse need:
|
|
||||||
- **Redmine**: The "Continue with KNEL Cloud" button (`#login-oauth-submit-1`) was never actually clicked by the code. The SSO flow may work if the button is clicked properly. The OIDC consent redirect-back-to-login issue needs investigation.
|
|
||||||
- **Discourse**: Need to open login modal (click "Log In" button), then find the SSO button inside the modal. Do a DOM dump of the modal.
|
|
||||||
|
|
||||||
### Phase 3: API Key Extraction
|
|
||||||
|
|
||||||
- **Gitea**: Working (proven once). Needs cleanup of stale tokens before regenerating.
|
|
||||||
- **Redmine**: Needs DOM dump of `/my/account` after SSO login succeeds.
|
|
||||||
- **Discourse**: May need admin pre-creation. Discuss with Charles.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## URLs (All Real, Operational Systems)
|
|
||||||
|
|
||||||
| System | URL |
|
|
||||||
|---|---|
|
|
||||||
| Cloudron panel | `https://my.knownelement.com` |
|
|
||||||
| Gitea | `https://git.knownelement.com` |
|
|
||||||
| Redmine | `https://projects.knownelement.com` |
|
|
||||||
| Discourse | `https://community.turnsys.com` |
|
|
||||||
| Bitwarden | `https://pwvault.turnsys.com` |
|
|
||||||
|
|
||||||
## vp-techops Account Details
|
|
||||||
|
|
||||||
- Email: `tsgstaff-coo-vptechops@turnsys.com`
|
|
||||||
- Username: `vptechops`
|
|
||||||
- Invite link: `https://my.knownelement.com/setupaccount.html?inviteToken=6aa567a43d51004155fda075badb5ffab8502f6a92fae028f90f921859b9f89f&email=tsgstaff-coo-vptechops%40turnsys.com&username=vptechops`
|
|
||||||
- Cloudron access: Granted by Charles (Gitea, Redmine, Discourse, and others)
|
|
||||||
- 2FA: NOT enabled (hard blocker)
|
|
||||||
|
|
||||||
## Mistakes from Session 1 (Do Not Repeat)
|
|
||||||
|
|
||||||
1. **Never delete BW items to "fix" duplicates** — use `update_item()` to modify in place
|
|
||||||
2. **Do ONE comprehensive DOM dump** of each page before writing selectors, not one-at-a-time
|
|
||||||
3. **Always use `timeout` on Docker commands** — 30s reads, 120s standard, 300s deployments
|
|
||||||
4. **Test BW lifecycle with dummy values first** — don't test against real credentials
|
|
||||||
5. **Commit frequently** — don't let changes pile up
|
|
||||||
6. **Don't skip 2FA** to work on other things — it's a hard compliance blocker
|
|
||||||
7. **Use `page.keyboard.type()` not `page.fill()`** for Vue/Pankow forms
|
|
||||||
8. **Use `[role="button"]` not `button`** for Cloudron Pankow UI elements
|
|
||||||
9. **Em dashes (U+2014) break Python** — use `--` instead in all source code
|
|
||||||
10. **The host bw CLI and container bw CLI share the same Vaultwarden account** — state diverges because they have separate local caches. Always `bw sync` before reads.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Suggested First Actions
|
|
||||||
|
|
||||||
1. Read `BASELINE-PROMPT.md` and `AGENTS.md` in TSYSGroupAIOS
|
|
||||||
2. Verify host `bw` works: `bw status`, `bw list items`
|
|
||||||
3. Fix the provisioner container BW state sync issue
|
|
||||||
4. Write a dummy BW lifecycle test (create, read, update, verify persistence)
|
|
||||||
5. Once BW is solid, do ONE DOM dump script that captures all 8 pages
|
|
||||||
6. Write all selectors from those dumps
|
|
||||||
7. Test 2FA enablement
|
|
||||||
8. Test full provisioning run
|
|
||||||
|
|||||||
Reference in New Issue
Block a user