Link to state-hub and hosteurope integration

This commit is contained in:
tegwick 2026-03-08 23:28:59 +01:00
parent 03f1d35ff4
commit 5187e63504
2 changed files with 425 additions and 0 deletions

214
CLAUDE.md Normal file
View file

@ -0,0 +1,214 @@
# RailianceHosts — Claude Code Instructions
## Custodian State Hub Integration
This project is tracked as the **railiance** domain in the Custodian State Hub.
Hub topic ID: `ca369340-a64e-442e-98f1-a4fa7dc74a38`
The State Hub runs locally at http://127.0.0.1:8000. The MCP server (`state-hub`)
exposes tools for reading and writing state without touching the API directly.
---
### Session Protocol
**On receiving your first message — before writing any response text — execute
this orientation sequence. Do not greet, do not ask what to do first.**
**Step 1 — Call the State Hub**
```
get_domain_summary("railiance") # workstreams, blocking decisions, recent progress, SBOM status
```
If the call fails, the API is offline: `cd ~/the-custodian/state-hub && make api`
**Step 2 — Scan local workplans**
Read every `.md` file under `workplans/`. Use `Glob(pattern="**/*.md", path="workplans/")`
or Bash `ls workplans/` to discover them. For each file with `status: active`,
extract and note:
- The workplan title and ID
- All tasks whose `status` is `todo` or `in_progress`
**Step 3 — Present orientation to the user**
Output a concise brief covering:
1. **Active workstreams** (from state hub) for the `railiance` domain — title,
task counts, any blocking decisions
2. **Pending tasks for this repo** — from local `workplans/` files (Step 2)
plus any state hub tasks with `[repo:railiance-hosts]` in their title
3. **Goal guidance** — if the summary contains a `goal_guidance` key, act on it:
- **`needs_workplan`** entries: for each active repo goal with no linked workstream,
surface it as the top suggested action — *"Repo goal '{title}' has no workplan yet.
Suggest: create workplans/RAIL-HO-WP-NNNN-<slug>.md and register a workstream
with repo_goal_id='{goal_id}'"*. Treat this as higher priority than continuing
existing work unless Bernd says otherwise.
- **`alignment_warnings`** entries: if active workstreams exist but are not linked
to the current repo goal, name the most recently active one and note:
*"Current work on '{recent_workstream_title}' may not be aligned with the active
goal '{active_goal_title}'. Continue unless you hear otherwise — but flag it."*
4. **Suggested next action** — the highest-priority open item across all sources,
with goal alignment taken into account
5. **SBOM status** — is `last_sbom_at` set for this repo? If not, note it as a gap
If there are no workstreams at all: follow the First Session Protocol below.
**During work:**
- Use `record_decision()` for any decision that affects direction or dependencies.
- Use `add_progress_event()` for notable events (milestones, blockers, insights).
- Use `resolve_decision()` to close a decision once the choice is made.
> **Design boundary:** The State Hub is a *read model*. Two write operations are
> permanently sanctioned: **Resolving Decisions** and **Suggesting Next Steps**.
> The bootstrap tools (`create_workstream`, `create_task`, `update_task_status`)
> are only for First Session Protocol. Formal work structure — workplans, tasks —
> belongs in the domain repo as files (ADR-001), not managed through the hub alone.
**At the end of every session:**
- Call `add_progress_event()` with a summary of what was accomplished or decided.
Include `topic_id: ca369340-a64e-442e-98f1-a4fa7dc74a38` and the relevant `workstream_id`.
---
### Repo Boundary Rule
This agent is responsible for files **in this repo only**.
- **Do not** write files or make commits in any other repository
- **Do not** create workplan files in other repos on their behalf
- When you identify work for another registered repo (**ecosystem todo**):
create a state hub task with `[repo:<slug>]` in the title — the other repo's
agent will see it at session start and create its own workplan
- When you identify work for an upstream repo (**third-party todo**):
create a contribution artifact in `contrib/` and register it
Terminology and workflows: `http://localhost:3000/docs/inter-repo-communication`
---
### First Session Protocol
Triggered when `get_domain_summary("railiance")` shows **no workstreams** for the `railiance`
topic. The project is registered but work has not yet been structured.
**Step 1 — Understand the project (read, don't write)**
- `~/the-custodian/canon/projects/railiance/project_charter_v0.1.md` — purpose, scope
- `~/the-custodian/canon/projects/railiance/roadmap_v0.1.md` — planned phases
- Scan the repo root: README, directory structure, existing code or docs
**Step 2 — Survey in-progress work**
- Look for TODOs, open branches, half-finished files, notes
- Note what is already done vs. what is clearly started but incomplete
**Step 3 — Propose workstreams to Bernd**
Propose 13 workstreams — each a coherent strand of work lasting weeks to months,
named clearly, anchored to a roadmap phase. **Wait for approval before creating.**
**Step 4 — Create workplan file first, then DB record**
Per ADR-001, work items originate as files in the repo:
```
workplans/RAIL-HO-WP-NNNN-<slug>.md ← write this first
```
Then register in the hub:
```
create_workstream(topic_id="ca369340-a64e-442e-98f1-a4fa7dc74a38", title="...", owner="...", description="...")
create_task(workstream_id="<id>", title="...", priority="high|medium|low")
```
**Step 5 — Record the setup**
```
add_progress_event(
summary="First session: structured railiance work into N workstreams, M tasks",
event_type="milestone",
topic_id="ca369340-a64e-442e-98f1-a4fa7dc74a38",
detail={"workstreams": [...], "tasks_created": M}
)
```
---
### Workplan Convention (ADR-001)
Work items MUST originate as files in this repo before being registered in the hub.
**File location:** `workplans/<ID>-<slug>.md`
**Frontmatter required:** `id`, `type: workplan`, `domain`, `repo`, `status`,
`state_hub_workstream_id`, `state_hub_task_id` (per task)
When another domain's agent identifies work for this repo, it creates a state hub
task with `[repo:railiance-hosts]` in the title (an **ecosystem todo**). You will
see it at session start via `get_domain_summary("railiance")`. When you pick it up, create
the corresponding workplan file in `workplans/` (ADR-001) and begin work.
---
### Contribution Tracking
Track upstream contributions in `contrib/` — bug reports (BR), feature requests
(FR), extension-point proposals (EP), upstream PRs (UPR).
```
contrib/
bug-reports/ # br-YYYY-MM-DD--org--repo--slug.md
feature-requests/ # fr-YYYY-MM-DD--org--repo--slug.md
extension-points/ # EP-RAIL-NNN--org--repo--slug.md
upstream-prs/ # upr-YYYY-MM-DD--org--repo--slug.md
```
Templates: `~/the-custodian/canon/standards/contrib-templates/`
Convention: `~/the-custodian/canon/standards/contribution-convention_v0.1.md`
```
register_contribution(type="br|fr|ep|upr", title="...", target_org="...",
target_repo="...", body_path="contrib/...", related_workstream_id="<uuid>")
update_contribution_status(contribution_id="<uuid>", status="submitted")
```
---
### SBOM
After updating dependencies, re-ingest the SBOM:
```bash
cd ~/the-custodian/state-hub
make ingest-sbom REPO=railiance-hosts SCAN=1 REPO_PATH=$(pwd)
```
Check compliance: `http://localhost:3000/repos`
Standard: `~/the-custodian/canon/standards/sbom-convention_v0.1.md`
---
### Remote Execution & State Hub Tunnel
This repo is designed to be worked on **from the HostEurope server** (or any
remote Linux box with access to the managed hosts). The State Hub runs locally
on Bernd's workstation at `127.0.0.1:8000` and is not publicly reachable.
**Before SSHing to the remote server, start a reverse tunnel on your local machine:**
```bash
ssh -R 8000:127.0.0.1:8000 <user>@<remote-host>
```
This forwards the remote's `localhost:8000` back to your local State Hub.
Claude on the remote host then reaches the MCP server and `get_domain_summary`
work as normal.
**Verify the tunnel is live from the remote:**
```bash
curl http://127.0.0.1:8000/state/health
# expected: {"status":"ok"}
```
**If the tunnel is not up (degraded mode):**
The State Hub call in Step 1 will fail. In that case:
- Skip Step 1 — proceed from local workplans only (Step 2)
- Note that goal guidance and progress logging will be unavailable
- Log any progress events manually from your local machine after the session
---
### Quick Reference
`~/the-custodian/state-hub/mcp_server/TOOLS.md` — compact MCP tool reference

View file

@ -0,0 +1,211 @@
---
id: RAIL-HO-WP-0001
type: workplan
title: "Secure Single-Server Bootstrap at HostEurope"
domain: railiance
repo: railiance-hosts
status: active
owner: railiance
topic_slug: railiance
repo_goal_id: 9e835b82-acca-493a-943f-2553ffe0bf54
state_hub_workstream_id: "bf40b47e-be5b-4930-a7d2-362e76b943bb"
created: "2026-03-08"
updated: "2026-03-08"
handoff_note: >
T01 and T02 (inventory entry and SSH tunnel setup) are prerequisites to run
T03 onwards from the HostEurope server itself. The ansible work previously
done in railiance-bootstrap (harden.yml, bootstrap.yml) is superseded by
the roles/base and roles/sops_agent structure in this repo. The HostEurope
server IP is 92.205.62.239 (hosts.ini is gitignored — recreate from
inventory/servers.yaml when working on the host).
---
# Secure Single-Server Bootstrap at HostEurope
## Goal
Provision and converge the HostEurope server so that it becomes a secure,
hardened node ready to join the ThreePhoenix Kubernetes cluster. This workplan
covers everything from establishing SSH access and tunnel connectivity through a
fully converged, verified server — all secrets managed via SOPS/age, all
services secured from the start.
Scope is deliberately narrow: one server, secure from day one. Automated
provisioning of additional server resources and the full three-node setup
are deferred.
## Boundary conditions
- Ubuntu 24.04 LTS at HostEurope (92.205.62.239), manually provisioned
- All remote access key-based only (no password auth)
- Firewall active before k3s is installed
- Secrets managed via SOPS/age — nothing committed in plaintext
- This workplan is **run from the HostEurope server** via SSH, not from WSL
## Remote execution setup
This repo is intended to run from the HostEurope server itself. To retain
State Hub connectivity during Claude sessions on that server, set up an SSH
reverse tunnel from your local machine before connecting:
```bash
# On your local machine — forward local state-hub to the remote
ssh -R 8000:127.0.0.1:8000 <user>@92.205.62.239
```
Then Claude on the remote host can reach `http://127.0.0.1:8000` as normal.
See also: the tunnel setup note in `CLAUDE.md`.
---
## Tasks
### T01 — Add HostEurope host to inventory
```task
id: T01
status: done
completed: "2026-03-08"
priority: high
state_hub_task_id: "9b2222a3-0f9f-4543-9321-e4cd5f87a457"
```
Add the HostEurope host to `inventory/servers.yaml` under a `hosteurope` group.
Create `ansible/hosts.ini` from the inventory (gitignored — recreate on each
new working machine):
```bash
python3 ansible/inventory_from_yaml.py > ansible/hosts.ini
# or add manually: [hosteurope]\n92.205.62.239 ansible_user=...
```
Verify connectivity:
```bash
ansible -i ansible/hosts.ini hosteurope -m ping
```
**Done when:** ping succeeds from a control node with network access to the host.
---
### T02 — Set up SSH tunnel for State Hub access
```task
id: T02
status: todo
priority: high
state_hub_task_id: "e4dda416-19bc-4672-b9ba-8ddb1b9e9659"
```
On your local machine, establish a reverse tunnel before SSH-ing to the
HostEurope server so the State Hub MCP server is reachable from Claude
sessions on that host:
```bash
ssh -R 8000:127.0.0.1:8000 <user>@92.205.62.239
```
Verify the tunnel is working from the remote:
```bash
curl http://127.0.0.1:8000/state/health
```
**Done when:** health check returns `{"status":"ok"}` from the HostEurope server.
---
### T03 — Extend base role with security hardening
```task
id: T03
status: todo
priority: high
state_hub_task_id: "6eda6875-1301-4794-a07e-3e13ff1d92bf"
```
Extend `ansible/roles/base/tasks/main.yml` to cover security hardening that
must run before any service installation:
- Disable root SSH login (`PermitRootLogin no`)
- Disable password authentication (`PasswordAuthentication no`)
- Enable and configure UFW: deny all inbound, allow SSH (22), k3s API (6443),
Flannel VXLAN (8472/UDP)
- Install and enable `fail2ban` with SSH jail
- Set `HISTCONTROL=ignorespace` in `/etc/profile.d/`
Verify with:
```bash
ansible-playbook -i ansible/hosts.ini -l hosteurope ansible/playbooks/bootstrap.yaml --check
```
**Done when:** dry-run produces no errors and hardening tasks are visible in
the play recap.
---
### T04 — Run bootstrap on the HostEurope host
```task
id: T04
status: todo
priority: high
state_hub_task_id: "77921431-3a45-45b2-a0b0-cf0c43262205"
```
Execute the full bootstrap playbook from the HostEurope server (tunnel must
be active per T02):
```bash
ansible-playbook -i ansible/hosts.ini -l hosteurope ansible/playbooks/bootstrap.yaml
```
**Done when:**
- Playbook completes with no failed tasks
- UFW is active and SSH still works after the run
- SOPS agent role applied successfully
---
### T05 — Smoke test and record
```task
id: T05
status: todo
priority: medium
state_hub_task_id: "c573c200-bf22-49d1-86f9-dca1fc71743c"
```
Verify the converged state:
```bash
# Confirm UFW active
ansible -i ansible/hosts.ini hosteurope -m shell -a "ufw status"
# Confirm fail2ban running
ansible -i ansible/hosts.ini hosteurope -m shell -a "systemctl is-active fail2ban"
# Confirm SSH hardening applied
ansible -i ansible/hosts.ini hosteurope -m shell -a "sshd -T | grep -E 'permitrootlogin|passwordauthentication'"
```
Add `docs/hosteurope-bootstrap.md` recording:
- Server specs (vCPU, RAM, disk)
- Public IP (no credentials)
- Date bootstrapped
- Roles applied
Log completion to the State Hub:
```
add_progress_event(summary="HostEurope server bootstrapped and hardened",
event_type="milestone", workstream_id="bf40b47e-be5b-4930-a7d2-362e76b943bb")
```
**Done when:** all checks pass and the progress event is logged.
---
## References
- Repo goal: `9e835b82-acca-493a-943f-2553ffe0bf54`
- Domain goal: `6f96c712-60e6-4ea9-ab06-168878eafbce` (Three-Phoenix Secure Kubernetes Infrastructure)
- Previous ansible work: `railiance-bootstrap/ansible/` (harden.yml, bootstrap.yml — superseded by this repo's role structure)