feat(workplan): CUST-WP-0012 multi-user onboarding and environment bootstrap
Captures friction points surfaced during RAIL-PL-WP-0001 T01 execution: missing SSH key, no credential.helper, manual MCP registration, no bootstrap script. Collects them into a repeatable onboarding journey. Tasks: - T01: git credential.helper for Gitea (libsecret/store/cache) - T02: SSH key generation and host authorization automation - T03: Claude Code MCP registration make target - T04: idempotent bootstrap-env.sh (high priority) - T05: onboarding guide (operator + collaborator personas) - T06: state hub multi-user model (deferred, low priority) Workstream: a28d9e29-4119-4b73-9469-f921920253ef Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
246
workplans/CUST-WP-0012-multi-user-onboarding.md
Normal file
246
workplans/CUST-WP-0012-multi-user-onboarding.md
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
---
|
||||||
|
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 <config>`
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user