a43ff2de625d37e42a6981c1e1d971dad4ef69c6
Operator decision: backend env hard-coding a single guild/channel was
wrong because (a) one Center can host many guilds and (b) one guild
can have many announce channels for different purposes. The
proposing agent now chooses where this topic's lifecycle events go,
passed as create-topic params and stored on the topic row.
Schema migration 002:
- ALTER topics ADD announce_guild_base_url VARCHAR(255) NULL,
announce_channel_id VARCHAR(64) NULL.
- Both nullable; one-of-two is rejected at POST time; both null =
topic creator opted out of broadcasts (announcer skips with log).
handlers/topics.go: createTopicBody adds announce_guild_base_url +
announce_channel_id; validates both-or-neither.
fabric/announce.go: rewritten signature. NewAnnouncer takes only
the system api key. PostTopicAnnouncement + PostLifecycleEvent take
a Target {GuildBaseURL, ChannelID} per call. Zero-value Target -> skip.
orchestrator/ticker.go: new helper topicTarget(topic) extracts the
target from the topic row; all broadcasts route through it.
verdict.go: same per-topic target extraction at completion.
config: removed FabricGuildBaseURL, FabricAnnounceChannelID,
FabricBotBearerToken from the Config struct + env reads.
FabricSystemAPIKey env renamed to DIALECTIC_FABRIC_SYSTEM_API_KEY
to disambiguate from the Fabric backend's own
FABRIC_BACKEND_GUILD_SYSTEM_API_KEY (operator: paste the same value
into both - one says "I am the system caller", the other says "I
accept this caller as system").
FABRIC_BOT_BEARER_TOKEN is gone entirely. The upgraded Guild
ApiKeyGuard accepts x-fabric-system-key alone for announce posts;
no per-user Bearer needed. Pairs with the matching change on
nav/Fabric.Backend.Guild commit 985b06a.
Dialectic.Backend — v2 (Go)
Greenfield Go rewrite of the Python v1 backend. Agent-native debate
platform per /home/hzhang/arch/DIALECTIC-V2-DESIGN.md.
Python v1 history is preserved on branch archive/python-v1.
What's here (Phase 2A + 2B + 2C, 2026-05-23)
| Subsystem | Status |
|---|---|
HTTP server (chi router) |
✅ |
Config from env (internal/config) |
✅ |
MySQL via sqlx + embedded SQL migrations |
✅ |
Schema: topics, signups, camps, rounds, arguments, verdicts, agent_keys, system_keys, verdict_schemas |
✅ |
| Auth middlewares: agent bearer (real), OIDC browser (Phase 2 stub w/ dev bypass) | ✅ |
/api/healthz |
✅ |
/api/topics list / get / create / set-visibility |
✅ |
/api/topics/{id}/signups list / create (agent self-enroll) |
✅ |
| Orchestration engine (camp allocation, round driver, judge invocation) | ⬜ Phase 2D |
| SSE live transcripts | ⬜ Phase 2D |
| Full OIDC + Keycloak JWKS verification | ⬜ Phase 4 |
| Nginx + CF Origin Cert on server.t3 | ⬜ Phase 2E |
Layout
main.go entrypoint (load → wire → serve)
go.mod
Dockerfile
docker-compose.dev.yml backend + mysql for local iteration
internal/
config/ 12-factor env loader
db/
db.go sqlx + embedded migration runner
migrations/001_init.sql v2 schema, idempotent
models/ entity types (sqlx + json tags)
store/ query layer (per-entity)
auth/ agent api-key + oidc middlewares
httpapi/
routes.go chi router + auth chains
handlers/ per-endpoint handlers
Run locally
docker compose -f docker-compose.dev.yml up --build
# backend on http://localhost:8090
curl http://localhost:8090/api/healthz
Env vars (see internal/config/config.go for the full list):
| Var | Default (dev) | Required in prod |
|---|---|---|
ENV_MODE |
dev |
must be prod |
HTTP_ADDR |
0.0.0.0:8090 |
— |
CORS_ALLOW_ORIGINS |
* |
concrete list (no *) |
DB_HOST/PORT/NAME/USER/PASSWORD |
dev defaults | ✓ password required |
AGENT_API_KEY_PEPPER |
— | ✓ |
OIDC_ISSUER / OIDC_CLIENT_ID |
— | ✓ |
OIDC_DEV_BYPASS_TOKEN |
unset | ignored in prod |
SYSTEM_API_KEY |
unset | populate when announce-channel push lands |
Dev bypass for browser routes
In ENV_MODE=dev with OIDC_DEV_BYPASS_TOKEN=<token> set:
curl -H "x-dev-bypass: <token>" http://localhost:8090/api/topics
# attached as user 'dev-operator' with role 'dialectic-admin'
In prod, this header is ignored regardless of value.
Agent bearer for plugin routes
The OpenClaw plugin (Dialectic.OpenclawPlugin, Phase 3) calls with:
Authorization: Bearer <raw-agent-api-key>
The key is hashed with AGENT_API_KEY_PEPPER and matched against
agent_keys.key_hash. To provision an agent's key (Phase 3 will add a
proper hf user create-dialectic-key CLI; for now, manual SQL):
INSERT INTO agent_keys (agent_id, key_hash)
VALUES ('manager', SHA2(CONCAT('<pepper>:', '<raw>'), 256));
What's next
- Phase 2D: camp allocation algorithm + round driver + judge invocation. Wired to Fabric announce channel (via system-api-key) + the Dialectic.OpenclawPlugin's tool for agent argument submission.
- Phase 2E: nginx config + CF Origin Cert + deploy to server.t3.
- Phase 3: Dialectic.OpenclawPlugin — agent-facing tools.
- Phase 4: frontend rewrite (STYLE.md + real Keycloak OIDC + visibility toggle UI).
- Phase 5: end-to-end integration with
analyze-intelworkflow.
Description
Languages
Go
99.2%
Dockerfile
0.8%