guildhall/CLAUDE.md
Tyler J King 38cf2b7c6b feat(orchestrator): governance correctness — override revocation + bootstrap ceremony
Wire founding override enforcement (TTL guard, periodic sweep, second-
master auto-revoke, manual revocation) and replace the approve stub with
a real Ed25519 signing flow through two bootstrap modes (self-sovereign
and partner-hosted with Guildhouse as default partner).

Pipeline now pauses at awaiting_approval, returns schematic_hash for the
signer, and resumes via POST /api/approvals webhook. HostingAgreement
table + HostingCeremony module support partner-hosted onboarding with
auto-ratification for Guildhouse-as-partner.

70 tests, 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
2026-05-16 12:54:55 -04:00

8.4 KiB
Raw Blame History

Guildhall — Claude Context

What this is

Phoenix umbrella app — ceremony orchestrator + governance UI for the Guildhouse platform. Onboards guilds (MSP/ISV/NSP organizations), deploys founding schematics via gRPC, and manages membership with ceremony-engine approval flows.

Architecture

guildhall/
├── apps/
│   ├── guildhall_ops_db      — Ecto schemas + contexts (Postgres)
│   ├── guildhall_orchestrator — gRPC clients, GenServers, template engine
│   ├── guildhall_web          — Phoenix LiveView UI + OIDC auth
│   ├── guildhall_chronicle    — Chronicle event consumer (stub)
│   └── guildhall_graph_bridge — Microsoft Graph reconciler (stub)
├── config/                    — dev.exs, runtime.exs (env-var driven)
└── k8s/                       — Kubernetes manifests (00-93, numeric order)

External services (gRPC)

Service Port Proto Client module
ceremony-service (Rust) 50053 ceremony.v1.CeremonyService Guildhall.Orchestrator.CeremonyClient
ffc-schematic-server (Rust) 9091 schematic.v1.SchematicsService + FfcSchematicService Guildhall.Orchestrator.SchematicClient

Env vars: CEREMONY_SERVICE_URL, SCHEMATIC_SERVICE_URL, FFC_SCHEMATIC_SERVICE_URL. In-cluster DNS: <name>.guildhall.svc.cluster.local:<port>.

Auth

Keycloak OIDC at auth.guildhouse.dev/realms/guildhouse, client guildhall-web. Env vars: OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI.

  • GuildhallWeb.Plugs.Authfetch_current_user/2 plug (reads session)
  • GuildhallWeb.AuthController — login/callback/logout controller
  • GuildhallWeb.AuthHooks — LiveView on_mount(:require_auth)
  • User stored in session as map: %{"did" => "did:web:...", "email" => ..., "name" => ..., "sub" => ..., "preferred_username" => ...}
  • DID format: did:web:guildhouse.dev:user:<preferred_username>
  • Hub operator DID: did:web:guildhouse.dev:user:tking

Database tables (guildhall_ops_db)

Pre-existing: governed_artifacts, deployment_states, verification_results

New guild tables:

  • guildsguild_id (10-bit, 0x0100x3FF), slug, guild_type (msp/isv/nsp), status (pending_approval/approved/denied/active/suspended), registration_ceremony_id, trust_domain, registrant_did, contact_did
  • guild_schematics — FK to guilds, template_name, schematic_name, schematic_version, realization_id, status (pending/draft/validated/hosting_agreed/awaiting_approval/approved/published/realizing/realized/partially_realized/failed), realization_snapshot (map), bootstrap_mode (self_sovereign/partner_hosted), founding_override_expires_at, founding_override_revoked_at, founding_override_revocation_reason, approval_requested_at, approved_at, approval_metadata (map)
  • guild_memberships — FK to guilds, user_did, role (apprentice/journeyman/master), status (pending/approved/denied/active/suspended/removed), membership_ceremony_id, unique on (guild_id, user_did)
  • hosting_agreements — FK to guild_schematics + guilds (partner), hosting_did, terms (map), partner_signature (binary, Ed25519), status (proposed/ratified/active/handoff_pending/completed/expired), auto_ratified, expires_at

Key flows

Guild registration

  1. User fills form at /guilds/register → creates guild row (status: pending_approval) + ceremony via CeremonyClient.create_guild_registration_ceremony
  2. Hub operator (tking) sees pending guild at /guilds/:slug, clicks Approve → CeremonyClient.approve_ceremony + Guilds.approve_guild/1 (Ecto.Multi: updates guild to "approved" + creates registrant as guild master)

Schematic deployment (FfcPipeline)

  1. Guild master visits /guilds/:slug/schematic → loads TOML template for guild type
  2. Click Deploy → FfcPipeline.deploy/2 runs 11 steps: load → validate → resolve → validate_resolved → encode → create_on_server → validate_on_server → create_db_record → fetch_schematic_hash → check_founding_override → check_hosting_agreement
  3. Pipeline pauses at awaiting_approval — returns {:paused, state} with schematic_hash for signing
  4. External signer POSTs Ed25519 signature to POST /api/approvals (bearer token auth)
  5. ApprovalCoordinator validates signer + calls FfcPipeline.resume_after_approval/2: approve_rpc → publish → realize → update_db → start_poller
  6. /guilds/:slug/realization shows 7 reconciler sections via PubSub live updates

Bootstrap modes

  • self_sovereign — Guild has own FFC. Founding master signs from their own GSH.
  • partner_hosted — Partner guild (default: Guildhouse guildhouse-ops) hosts FFC during bootstrap. Requires ratified HostingAgreement. Guildhouse auto-ratifies for NSP tiers.

Founding override lifecycle

  • NSP templates use founding_override to reduce multi-party quorum to 1 during bootstrap
  • 90-day TTL enforced by FoundingOverrideGuard (inline check) + FoundingOverrideMonitor (hourly sweep)
  • Revoked automatically when second master onboards, or manually by founding master on GuildLive.Show

Member onboarding

  1. User visits /guilds/:slug/join → creates membership (pending) + ceremony for guild master approval
  2. Guild master at /guilds/:slug/members → approve/deny via ceremony engine, manage roles

Routes

All guild routes are under authenticated live_session :authenticated:

/                          DashboardLive
/guilds                    GuildLive.Index
/guilds/register           GuildLive.Register
/guilds/:slug              GuildLive.Show
/guilds/:slug/schematic    GuildLive.Schematic
/guilds/:slug/realization  GuildLive.Realization
/guilds/:slug/join         GuildLive.Join
/guilds/:slug/members      GuildLive.Members
/ceremonies                CeremonyLive.Index
/artifacts                 ArtifactLive.Index

Public: /auth/login, /auth/callback, /auth/logout, /health

API (bearer token GUILDHALL_APPROVAL_WEBHOOK_SECRET):

POST /api/approvals                       — receive Ed25519 signature for pending schematic
POST /api/hosting-agreements/:id/ratify   — partner ratifies hosting agreement

Orchestrator supervision tree

  • Guildhall.Orchestrator.CeremonyOrchestrator — existing ceremony workflow coordinator
  • Guildhall.Orchestrator.RealizationPoller — GenServer, polls realization status every 5s for watched IDs, broadcasts on "realization:#{guild_slug}" PubSub topic
  • Guildhall.Orchestrator.FoundingOverrideMonitor — GenServer, hourly sweep of expired overrides, broadcasts {:founding_override_revoked, ...} on "override:#{slug}"
  • Guildhall.Orchestrator.ApprovalCoordinator — GenServer, tracks pending approvals by "name:version", dispatches resume_after_approval when signature received

Schematic templates

TOML files in apps/guildhall_orchestrator/priv/schematic_templates/:

  • msp-founding.toml — attestation tier 2, MFA, single_approval
  • isv-founding.toml — attestation tier 1, no MFA, single_approval
  • nsp-founding.toml — attestation tier 3, MFA + hardware, multi_party quorum 2

SchematicTemplate.render_template/2 substitutes {{guild_slug}}, {{guild_name}}, {{trust_domain}}, {{registrant_did}}.

Generated protobuf modules

In apps/guildhall_orchestrator/lib/guildhall/orchestrator/proto/:

  • ceremony/v1/ceremony.pb.exCeremony.V1.CeremonyService.Stub
  • schematic/v1/schematics.pb.exSchematic.V1.SchematicsService.Stub
  • schematic/v1/ffc_schematic.pb.exSchematic.V1.FfcSchematicService.Stub

These were generated with protoc --elixir_out=plugins=grpc. Re-generate if protos change.

K8s manifests

Files in k8s/ applied in numeric order. Key additions:

  • 90-ceremony-service-deployment.yaml + 91-*-service.yaml — ClusterIP on 50053
  • 92-schematic-server-deployment.yaml + 93-*-service.yaml — ClusterIP on 9091
  • 70-guildhall-deployment.yaml — includes OIDC + gRPC env vars
  • Secrets created imperatively (see 50-guildhall-secrets-template.yaml)

Commands

mix deps.get              # fetch deps
mix ecto.migrate          # run migrations
mix compile               # verify compilation
mix phx.server            # start dev server (localhost:4000)
mix test                  # run tests

Commit convention

Sign commits as tking@guildhouse.dev:

git -c user.email=tking@guildhouse.dev commit -s -m "message"