--- id: CUST-WP-0012 type: workplan title: "Multi-User Onboarding and Environment Bootstrap" domain: custodian repo: the-custodian status: active owner: custodian topic_slug: custodian state_hub_workstream_id: "a28d9e29-4119-4b73-9469-f921920253ef" created: "2026-03-11" updated: "2026-03-11" --- # Multi-User Onboarding and Environment Bootstrap ## Goal Make the Custodian system accessible to collaborators beyond the primary operator. A new user (or a new machine for the existing operator) should be able to go from zero to a productive Claude Code session with full State Hub MCP connectivity in a single session, without manual steps or undocumented tribal knowledge. ## Context Several friction points surfaced during the 2026-03-11 session: - No SSH key for Railiance01 on WSL2 → blocked `make tunnel-loop` - No `~/.railiance_gitea.conf` → blocked repo creation script - Token missing `read:user` scope → blocked org repo creation - No `git credential.helper` → credentials required on every push - MCP registration is manual and documented only in `CLAUDE.md` Each of these is a solved problem in isolation. This workstream collects them into a repeatable, documented bootstrap experience. ## Scope Two personas: | Persona | Access level | Typical machine | |---------|-------------|-----------------| | Primary operator | Full access, all domains | WSL2 workstation | | Domain collaborator | Read + write to one domain | COULOMBCORE, remote laptop | ## Tasks ### T01 — Git credential.helper for Gitea access ```task id: CUST-WP-0012-T01 state_hub_task_id: 71628269-9a75-4dae-a347-e64a86040322 status: todo priority: medium ``` Document and automate `git credential.helper` setup for Gitea (`http://92.205.130.254:32166`). Recommend `libsecret` (keyring-backed) on machines that support it; fall back to `credential.helper=store` (persistent, plaintext `~/.git-credentials`) on headless servers. Include in bootstrap script (T04) and onboarding guide (T05). ```bash # Preferred: libsecret (GNOME keyring, WSL2 with keyring daemon) sudo apt-get install -y libsecret-1-0 libsecret-1-dev sudo make -C /usr/share/doc/git/contrib/credential/libsecret git config --global credential.helper \ /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret # Fallback: store (plaintext, suitable for headless servers) git config --global credential.helper store # Headless server alternative: cache (in-memory, 1h timeout) git config --global credential.helper 'cache --timeout=3600' ``` **Done when:** included in bootstrap script; push to Gitea works without re-entering credentials on second attempt. --- ### T02 — SSH key generation and authorization automation ```task id: CUST-WP-0012-T02 state_hub_task_id: fea965e9-8a8f-439c-9096-8f7756eb71ed status: todo priority: medium ``` Script or Ansible task that: 1. Generates an `ed25519` key pair on the new machine if none exists 2. Displays the public key with copy instructions 3. Authorizes it on all managed hosts (Railiance01, COULOMBCORE) via `ssh-copy-id` or Ansible `authorized_key` module Surfaced by: RAIL-PL-WP-0001 T01 — no SSH key on WSL2 blocked `make tunnel-loop HOST=tegwick@92.205.62.239`. ```bash # Generate if missing [[ -f ~/.ssh/id_ed25519 ]] || ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N "" # Authorize on a target host (requires existing access once) ssh-copy-id -i ~/.ssh/id_ed25519.pub tegwick@92.205.62.239 ssh-copy-id -i ~/.ssh/id_ed25519.pub tegwick@92.205.130.254 ``` **Done when:** included in bootstrap script; documented in onboarding guide. --- ### T03 — Claude Code MCP registration automation ```task id: CUST-WP-0012-T03 state_hub_task_id: 60318e9a-972e-45c8-afde-82ed0625f594 status: todo priority: medium ``` Automate the state-hub MCP server registration on a new machine. Currently this is a multi-step manual process documented in `~/.claude/CLAUDE.md`. It should be a single `make` target or script: ```bash # In the-custodian/state-hub/ make register-mcp # idempotent; safe to re-run ``` The script should: 1. Detect whether `state-hub` is already in `~/.claude.json` 2. Extract the server config from `.mcp.json` 3. Run `claude mcp add-json -s user state-hub ` 4. Run `patch_mcp_cwd.py` to restore the cwd field 5. Print instructions to restart Claude Code Should also detect whether the state hub is reachable directly (`http://127.0.0.1:8000`) or needs a tunnel (via ops-bridge), and emit a warning if neither is available. **Done when:** `make register-mcp` works on a clean machine; documented in onboarding guide. --- ### T04 — Environment bootstrap script ```task id: CUST-WP-0012-T04 state_hub_task_id: 84a94761-e424-4470-a9a2-64d9cabadb7f status: todo priority: high ``` Single idempotent script: `state-hub/scripts/bootstrap-env.sh` Checks/installs prerequisites and configures the environment: | Step | What | |------|------| | Prerequisites | git, sops, age, helm, kubectl, uv, claude CLI | | Git credential | `credential.helper` (libsecret or store) | | SSH key | Generate ed25519 if missing; display public key | | MCP registration | `make register-mcp` (T03) | | Gitea config | Prompt for token; write `~/.railiance_gitea.conf` | | Health check | `curl /state/health`; warn if tunnel needed | Design constraints: - Idempotent: safe to run on an already-configured machine - No silent failures: each step prints ✓ / ✗ / ⚠ - Minimal dependencies: bash + curl only to get started **Done when:** running the script on a clean Ubuntu 24.04 machine produces a working Custodian environment with no additional manual steps. --- ### T05 — Onboarding guide and user journey documentation ```task id: CUST-WP-0012-T05 state_hub_task_id: b0839802-659a-475b-8b84-ab7341ea3d15 status: todo priority: medium ``` Write `docs/onboarding.md` in the-custodian covering the full journey for both personas: **Primary operator (new machine):** 1. Prerequisites (git, SSH client) 2. Clone `the-custodian` 3. Run `make bootstrap-env` (T04) 4. Restart Claude Code → verify MCP is active 5. First session: `get_state_summary()` → orient → work **Domain collaborator (new person):** 1. Prerequisites + Gitea account 2. `ssh-copy-id` to get access to Railiance01 (or just COULOMBCORE) 3. Set up ops-bridge tunnel to reach state hub 4. Clone domain repo 5. First Claude Code session with MCP via tunnel 6. Contributing a workplan (ADR-001 convention) **Done when:** a new collaborator can follow the guide without clarification from the primary operator. --- ### T06 — State Hub multi-user model (deferred) ```task id: CUST-WP-0012-T06 state_hub_task_id: d5df3302-67b9-4765-a8d8-ea2df53dff6e status: todo priority: low ``` Design a lightweight user/role model for the state hub: | Role | Permissions | |------|-------------| | Primary operator | Full read/write, all domains | | Domain collaborator | Read all; write to own domain only | | Observer | Read-only | Decision needed: enforce at API layer (HTTP Basic / token auth per domain) or rely on Gitea repo permissions as the authoritative boundary (simpler — the hub is a read model anyway). **Deferred until:** first external collaborator is actively onboarding. Implement T01–T05 first; multi-user access control is only needed when there is more than one user. --- ## References - ops-bridge repo: `ops-bridge` (tunnel lifecycle management) - MCP registration: `~/.claude/CLAUDE.md` (current manual procedure) - Gitea repo creation: `railiance-cluster/tools/create_railiance_repo.sh` - ADR-001: workplans as repo artefacts - Surfaced by: RAIL-PL-WP-0001 T01 execution, 2026-03-11