All manifests now use git.guildhouse.dev/guildhouse/substrate/<component>:v0.2.0
instead of git.guildhouse.dev/tking/<component>:v0.1.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
The google_protos package redefines modules (Google.Protobuf.Any, etc.)
already provided by protobuf ~> 0.13, causing --warnings-as-errors to
fail the production release build.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
Covers architecture, auth flow, database schema, gRPC services,
routes, schematic templates, and K8s manifests so future sessions
can orient without re-discovering the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
Implements the full founding-guild onboarding stack across four phases:
Phase A — Keycloak OIDC auth pipeline (oidcc) + guild registration with
ceremony-engine approval (SingleApproval, hub operator approves via gRPC).
Phase B — Founding schematic templates (MSP/ISV/NSP TOML), gRPC clients
for ceremony-service and ffc-schematic-server, schematic fork/bind/realize
LiveView with DB audit trail in guild_schematics.
Phase C — RealizationPoller GenServer polling realization status every 5s,
PubSub broadcast, live realization dashboard showing 7 reconciler sections.
Phase D — Self-service member onboarding (join request → guild master
approval via ceremony), member management LiveView, auto-create guild
master on guild approval via Ecto.Multi transaction.
Includes K8s manifests for ceremony-service (port 50053) and
ffc-schematic-server (port 9091) as ClusterIP services, plus updated
guildhall deployment with OIDC and gRPC service URL env vars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
The Dockerfile's `mix release --overwrite` step failed because the
umbrella's root mix.exs lacked an explicit `releases:` block. Umbrella
projects cannot infer a default release — `mix release` requires the
operator to name which umbrella children go into each release and
their startup order.
Adds a single `:guildhall` release bundling all five umbrella apps as
:permanent, with startup order respecting the dep graph:
guildhall_ops_db (Repo — nothing else here starts without it)
guildhall_chronicle
guildhall_orchestrator
guildhall_graph_bridge
guildhall_web (HTTP endpoint last, after Repo + contexts)
Each app's own `in_umbrella` deps in apps/*/mix.exs will back-stop
the ordering via OTP's dependency graph regardless, but the explicit
list is clearer and matches standard Phoenix-umbrella release idiom.
Mistake was in the prior Dockerfile commit (c6f1d07) planning — the
Dockerfile comment claimed the release name could be inferred from
the umbrella, which is true for single-app projects but not umbrellas.
No Dockerfile change needed; the fix is entirely in mix.exs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
DEPLOY-EXPLORATORY documents the cluster state that shaped deployment
decisions (Keycloak as template, Hetzner LB + Cloudflare pattern, no
Postgres operator so sibling-Deployment pattern).
FORGEJO-REGISTRY-INVESTIGATION documents that the registry was already
operational in Forgejo 9.0.3 (packages enabled by default) and the
storage/credential path forward.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
Multi-stage Elixir/OTP Dockerfile, Kubernetes manifests following
Keycloak pattern, mix release migration module, and deploy runbook.
Target: guildhall.guildhouse.dev via Hetzner LB + Cloudflare (orange
cloud). Forgejo container registry at git.guildhouse.dev/tking/guildhall.
Not yet deployed; artifacts only. See DEPLOY-RUNBOOK.md for execution.
Artifacts produced:
- Dockerfile — multi-stage, Elixir 1.17.3 / OTP 27.1.2, debian-bookworm
builder + debian-bookworm-slim runtime. Dep-layer caching via
explicit apps/*/mix.exs copy before source. Asset pipeline runs
mix assets.setup + mix assets.deploy (tailwind + esbuild + phx.digest).
Non-root uid 1000, tini as pid-1, HEALTHCHECK against /health.
- .dockerignore — excludes _build/, deps/, k8s/, .git/, test artifacts,
and apps/guildhall_web/priv/static/assets/ (regenerated by phx.digest
inside the builder).
- apps/guildhall_web/.../router.ex — adds `/health` route under :api
pipeline. Unauthenticated by design (Kubernetes probes + LB target).
- apps/guildhall_web/.../controllers/health_controller.ex — shallow
health: Phoenix up + Ecto pool can `SELECT 1`. Returns 200 ok or 503
degraded with reason.
- apps/guildhall_ops_db/lib/guildhall/ops_db/release.ex — Release
module for migrations. `Guildhall.OpsDb.Release.migrate/0` and
`rollback/2`. Called from the migration Job via
`bin/guildhall eval`. Module path reflects actual repo location
(repo is `Guildhall.OpsDb.Repo` in `:guildhall_ops_db`, not the
prompt's suggested `Guildhall.Repo`).
Kubernetes manifests in k8s/ (numbered for apply order):
00-namespace.yaml — guildhall namespace w/ guildhouse labels
10-registry-secret-template.yaml — doc-only template for dockerconfigjson
20-postgres-pvc.yaml — 5Gi longhorn RWO
30-postgres-deployment.yaml — postgres:16, keycloak-matched resources
+ pg_isready probes, PGDATA subpath
40-postgres-service.yaml — ClusterIP :5432
50-guildhall-secrets-template.yaml — doc-only template for app + DB secrets
60-migration-job.yaml — ecto migration Job, name includes tag
for per-deploy uniqueness, TTL 24h
70-guildhall-deployment.yaml — RollingUpdate maxSurge 1 maxUnavailable 0,
/health probes, 200m/256Mi requests
and 1/1Gi limits, 5s preStop sleep
80-guildhall-service.yaml — LoadBalancer with exact Keycloak-
matched Hetzner annotations
(location nbg1, type lb11, name
guildhall, use-private-ip false),
port 80 origin (Cloudflare TLS)
- DEPLOY-RUNBOOK.md — 6-phase deploy sequence (build + push, cluster
prep, DB, migrate, app rollout, DNS + smoke), iteration helper with
sed-based tag-bump, rollback procedure (image rollback, schema
rollback via Release.rollback, full teardown), and v0.1 limitations
(Cloudflare-edge TLS not cluster-terminated; no Flux integration;
no OIDC wiring; no substrate CRD integration; single replica).
Decisions made during artifact production that weren't explicit in
the prompt:
- Release module name is `Guildhall.OpsDb.Release` (not
`Guildhall.Release`) matching the actual repo namespace. Migration
Job command adjusted to `Guildhall.OpsDb.Release.migrate()`.
- Dockerfile uses `-slim` builder variant (not the full bookworm
builder) to keep the builder stage closer to the runtime image
size, reducing multi-stage layer transfer during build.
- Asset compilation runs `mix assets.setup` before `mix assets.deploy`
so tailwind + esbuild binaries install cleanly inside the container
(the dev-only :runtime flag on those deps means they need explicit
install in a prod builder).
- tini added as pid-1 in the runtime stage. Not in the prompt, but
standard-practice for OTP containers to ensure signal propagation
and zombie reaping under Kubernetes.
- Rolling update strategy: maxSurge 1 / maxUnavailable 0 (zero-
downtime rollout at replicas=1; the new pod comes up alongside the
old, health-checks, then the old is terminated). Matches typical
single-replica LiveView pattern.
- preStop `sleep 5` — gives in-flight HTTP + LiveView connections a
grace window before termination.
- Hetzner LB annotations: verified exact set from cluster keycloak
service — location=nbg1, name=guildhall, type=lb11,
use-private-ip=false. The prompt asked about uses-proxyprotocol
and algorithm-type; neither is set on Keycloak's service and both
are omitted here for consistency.
- Migration Job name includes the tag (`guildhall-migrate-v0-1-0`) so
multiple deploys don't collide on Job name reuse. Runbook documents
the sed helper to bump both the image tag and the Job name for
subsequent deploys.
- Both exploratory docs (`DEPLOY-EXPLORATORY-2026-04-21.md`,
`FORGEJO-REGISTRY-INVESTIGATION-2026-04-21.md`) are currently
untracked in the repo. They're left out of this commit per the
prompt's explicit `git add` list. They can be committed separately
(or ignored) at Tyler's discretion.
Not done tonight (per prompt's NOT PERMITTED list):
- docker build / docker push
- kubectl apply of any manifest
- Forgejo PAT creation
- Cloudflare DNS changes
- git push (this commit is local-only pending review)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
README explains:
- guildhall = ceremony orchestrator + governance UI
- guildhall != ceremony engine (that's substrate)
- five umbrella apps with responsibilities
- local dev setup (mix deps.get + ecto.setup + phx.server)
- runtime config env vars (DATABASE_URL, SECRET_KEY_BASE,
PHX_HOST, POOL_SIZE, ECTO_IPV6)
- relationship to substrate, bxnet-ops, guildhouse-specs
- links to all relevant design docs
.forgejo/workflows/ placeholder for CI (future WS2 work).
Runtime config (config/runtime.exs) reads DATABASE_URL with
ECTO_IPV6 socket option support. Commented placeholders for
KUBECONFIG (substrate CRD watcher) and OIDC_* (Keycloak) are
explicit about what comes in future sprints.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
CeremonyOrchestrator: GenServer providing a PubSub broadcast
interface for ceremony status changes. LiveView subscribes to
these broadcasts for real-time updates. A K8s CRD watcher will
feed events into this in a future sprint; for now the init log
makes the stub state explicit.
Chronicle.Consumer: stub for the Ops DB projector that will
consume Chronicle events and hydrate the Ecto tables. Projector
design (idempotent, checkpointed, catch-up on restart) per
DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.5.
Both modules document the orchestrator/engine distinction:
guildhall orchestrates, substrate decides.
Both are now supervised by their respective application trees
(Guildhall.Orchestrator.Supervisor, Guildhall.Chronicle.Supervisor).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
Three LiveView modules reading from the Ops DB:
- DashboardLive at /: governance overview with artifact count,
healthy/drifted deployment states, and the five most recent
verification results. Subscribes to Guildhall.PubSub for
ceremony:* and posture:* topics.
- CeremonyLive.Index at /ceremonies: lists open ceremonies. Query
finds the latest custody_transition per artifact and keeps the
ones where to_state = 'ceremony_open'. PubSub-driven refresh.
Will integrate with substrate CRD watcher in a future sprint.
- ArtifactLive.Index at /artifacts: lists governed artifacts with
name, type, tier, truncated CID, and aggregate drift status
derived from their deployment_states.
Router updated to mount these LiveViews at /, /ceremonies,
/artifacts. Default PageController route removed.
Inline Heex templates (no separate .html.heex files); uses
Tailwind classes from Phoenix 1.8 default CSS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>
Direct translation of DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2:
- AccordBinding: governance context per artifact
- GovernedArtifact: registry with JSONB content + content_schema
- CustodyTransition: append-only chain of custody
- DeploymentState: current deployment per (artifact, target)
- VerificationResult: continuous attestation records
- ProjectorCheckpoint: Chronicle projector resume state
All JSONB fields use Ecto :map type (Postgres JSONB).
GIN indexes (jsonb_path_ops) on accord_terms and content columns.
Partitioning notes for time-series tables (DBA applies in prod).
Migrations renumbered to enforce FK dependency order
(accord_bindings → governed_artifacts → dependents).
Dev seed data (priv/repo/seeds.exs) creates nine governed artifacts
matching the JSONB content examples in the design doc §2.3
(Intune profile, DNS zone, DSC MOF, YANG config, Helm values,
JEA role, Conditional Access, TLS cert, OCI image) plus custody
transitions, deployment states, and verification results.
Verified: mix ecto.create + mix ecto.migrate + mix run seeds all
pass; 9 artifacts in governed_artifacts table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Tyler J King <tking@guildhouse.dev>