# 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: `.guildhall.svc.cluster.local:`. ## 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.Auth` — `fetch_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:` - 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: - **guilds** — `guild_id` (10-bit, 0x010–0x3FF), `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.ex` — `Ceremony.V1.CeremonyService.Stub` - `schematic/v1/schematics.pb.ex` — `Schematic.V1.SchematicsService.Stub` - `schematic/v1/ffc_schematic.pb.ex` — `Schematic.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 ```bash 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`: ```bash git -c user.email=tking@guildhouse.dev commit -s -m "message" ```