docs(P00): clarify architectural decisions
Record 5 PO-approved architectural decisions (all confidence >=0.8): - D-001: OpenAI-compatible BYOM contract for M1 (pluggable iface for M3) - D-002: Relay Agent in Go (single static binary for curl|bash) - D-003: AWS Secrets Manager (prod) + local-encrypted (dev) behind interface - D-004: Postgres append-only + hash-chain audit for M1 (S3 WORM in M3) - D-005: Next.js App Router + TypeScript single SPA No escalations. Pipeline proceeds to RESEARCH. ---ci--- phase: 0 milestone: v0.1 status: clarify ---/ci---
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"phase": 0,
|
||||
"stage": "specify",
|
||||
"stage": "clarify",
|
||||
"milestone": "v0.1",
|
||||
"phase_role": "pre_execution",
|
||||
"attempts": 1,
|
||||
"updated_at": "2026-08-24T22:40:00Z"
|
||||
"updated_at": "2026-08-24T22:45:00Z"
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Clarify — Architectural Decisions
|
||||
|
||||
Spec: CoreCI Chat v0.1 Engineering Specification v1.1 (locked 2026-08-24, Sarah Chen).
|
||||
Autonomy: `full` (decision threshold 0.6). All 5 decisions below were surfaced during kickoff and approved by the Product Owner before EXECUTE. Spec §7 resolved all 8 product-level open questions; the 5 decisions here are the remaining **architectural** choices the spec left to Engineering.
|
||||
|
||||
Each decision is recorded with: question, options considered, decision, rationale, confidence, status.
|
||||
|
||||
---
|
||||
|
||||
## D-001 — BYOM endpoint protocol contract
|
||||
|
||||
**Question:** Which wire protocol should the BYOM routing shim speak for M1, given customers may bring vLLM, TGI, Ollama, OpenAI, Azure OpenAI, Together, or self-hosted endpoints?
|
||||
|
||||
**Options considered:**
|
||||
- OpenAI-compatible `/v1/chat/completions` (universal interoperability)
|
||||
- Anthropic Messages API native
|
||||
- Pluggable provider interface with multiple impls from day one
|
||||
|
||||
**Decision:** **OpenAI-compatible `/v1/chat/completions` for M1**, with a pluggable `LlmProvider` interface so an Anthropic-native impl can be added in M3 without re-architecting the routing shim.
|
||||
|
||||
**Rationale:** The overwhelming majority of customer-hosted inference endpoints (vLLM, TGI, Ollama, OpenAI, Azure OpenAI, Together, LiteLLM) speak OpenAI-compatible. Native Anthropic support is a M3 concern; the interface keeps that door open without paying for it now. REQ-006/007/008/009 all reference a "test inference call" and "outbound traffic log" — OpenAI-compatible gives the cheapest validation path (one POST, JSON body, `choices[0].delta.content`).
|
||||
|
||||
**Confidence:** 0.85
|
||||
**Status:** approved (PO kickoff)
|
||||
**Affects:** REQ-006, REQ-007, REQ-008, REQ-009; ARCHITECTURE.md § apps/control-plane BYOM validator.
|
||||
|
||||
---
|
||||
|
||||
## D-002 — Relay Agent implementation language
|
||||
|
||||
**Question:** Should the Relay Agent be written in Go, Rust, or TypeScript (Node) given it is distributed via `curl|bash` and runs as a systemd service on Ubuntu 24.04 / Debian 12+?
|
||||
|
||||
**Options considered:**
|
||||
- Go — single static binary, zero runtime deps, tiny image, cross-compile trivial
|
||||
- Rust — single static binary, stronger safety, slower compile/iterate
|
||||
- Node/TypeScript — same language as control plane, but requires Node runtime on every customer host
|
||||
|
||||
**Decision:** **Go.**
|
||||
|
||||
**Rationale:** The install script ships a single static binary via `curl|bash`. Go gives that with zero customer-side runtime (no Node, no Python). Cross-compile to linux/amd64 + linux/arm64 is one command. systemd unit stays trivial (`ExecStart=/usr/local/bin/coreci-relay-agent`). The control plane stays TypeScript (Trigger.dev DX rationale from spec §7 Q1 is about the orchestration layer, not the agent). The SSH whitelist hook (M1) and the SSH adapter (M2) both run inside the agent; Go's `os/exec` + seccomp/pledge-style hardening is well-trodden.
|
||||
|
||||
**Confidence:** 0.9
|
||||
**Status:** approved (PO kickoff)
|
||||
**Affects:** REQ-010, REQ-011, REQ-012, REQ-013, REQ-026 (whitelist hook); ARCHITECTURE.md § apps/relay-agent.
|
||||
|
||||
---
|
||||
|
||||
## D-003 — Secret manager backend
|
||||
|
||||
**Question:** Which backend for `SecretProvider` given spec §5 names "AWS Secrets Manager or equivalent" and us-east-1 is the default region, while CI/local dev must run without AWS access?
|
||||
|
||||
**Options considered:**
|
||||
- AWS Secrets Manager only — simplest, but blocks CI/local
|
||||
- AWS Secrets Manager (prod) + local file (dev) — fast, but weak dev hygiene
|
||||
- AWS Secrets Manager (prod) + local-encrypted (dev) behind a `SecretProvider` interface — clean
|
||||
|
||||
**Decision:** **AWS Secrets Manager (prod, KMS-backed, us-east-1) + `LocalEncryptedProvider` (dev/test, AES-256-GCM) behind a `SecretProvider` interface.**
|
||||
|
||||
**Rationale:** Spec §5 mandates AWS Secrets Manager (or equivalent) for prod. The interface lets CI and local dev run without AWS credentials — `LocalEncryptedProvider` reads its master key from the ONE allowed env var (`SECRET_MASTER_KEY_DEV`), encrypts every tenant secret at rest with AES-256-GCM, and stores ciphertext in a gitignored local file. Prod swaps the impl via config. No tenant secret is ever in plaintext on disk, in a DB column, in a config file, or in logs — satisfying REQ-040 and the "no env vars for tenant secrets" rule. The DB stores only a reference (e.g. `aws-sm:coreci/<tenantId>/byom`).
|
||||
|
||||
**Confidence:** 0.85
|
||||
**Status:** approved (PO kickoff)
|
||||
**Affects:** REQ-040; ARCHITECTURE.md § packages/secrets.
|
||||
|
||||
---
|
||||
|
||||
## D-004 — Audit log storage backend for M1
|
||||
|
||||
**Question:** What is the M1 audit log store, given REQ-038 requires a "write-once store" and the immutability pattern set in M1 propagates to every M2/M3 event?
|
||||
|
||||
**Options considered:**
|
||||
- S3 Object Lock WORM from day one — strongest immutability, but adds infra + an async write path that complicates "write failure halts the operation" (Edge 7)
|
||||
- Postgres append-only table with hash-chain + REVOKE UPDATE/DELETE — cheap, synchronous, halt-on-fail is trivial
|
||||
- Dedicated append-only service (e.g. QuestDB, ClickHouse) — overkill for M1 volume
|
||||
|
||||
**Decision:** **Postgres append-only table `audit_log` with a hash-chain (`curr_hash = sha256(prev_hash || canonical_payload)`) and `REVOKE UPDATE, DELETE` from the app role. S3 Object Lock WORM is deferred to M3 hardening.**
|
||||
|
||||
**Rationale:** M1 volume is low (onboarding + dashboard events, no chat yet). A Postgres append-only table with a hash-chain gives cryptographic tamper-evidence, synchronous writes so "write failure halts" (Edge 7) is a single transaction, and `REVOKE UPDATE/DELETE` makes the app role physically unable to mutate rows. The hash-chain pattern is what propagates to M2/M3 — when we add S3 Object Lock in M3, the Postgres table stays as the hot path and S3 is the WORM cold store. Refactoring later is additive, not a rewrite. This matches the spec's "critical-path: append-only from day one" directive.
|
||||
|
||||
**Confidence:** 0.8
|
||||
**Status:** approved (PO kickoff)
|
||||
**Affects:** REQ-038; ARCHITECTURE.md § packages/audit, packages/db.
|
||||
|
||||
---
|
||||
|
||||
## D-005 — Web application framework
|
||||
|
||||
**Question:** Which framework for the browser surface, given M1 ships the admin dashboard and M3 ships the chat UI in the same product?
|
||||
|
||||
**Options considered:**
|
||||
- Next.js (App Router) + TypeScript, single SPA — one app for dashboard (M1) + chat (M3)
|
||||
- Separate Next.js apps (dashboard, chat) — clearer M1/M3 boundary, duplicate infra
|
||||
- Remix + TypeScript — similar DX, smaller ecosystem for SSE/streaming
|
||||
|
||||
**Decision:** **Next.js (App Router) + TypeScript, single SPA.** M1 ships the admin dashboard as server components; M3 adds the chat UI in the same app.**
|
||||
|
||||
**Rationale:** One app = one deploy, one auth flow, one RBAC map, one RLS-aware API gateway. The dashboard (M1) and chat (M3) share `packages/auth`, `packages/db`, `packages/audit` cleanly. App Router server components read via the API gateway (never bypassing RLS); M3's SSE streaming uses Route Handlers. TS-first aligns with the Trigger.dev rationale (spec §7 Q1).
|
||||
|
||||
**Confidence:** 0.85
|
||||
**Status:** approved (PO kickoff)
|
||||
**Affects:** ARCHITECTURE.md § apps/control-plane, apps/dashboard.
|
||||
|
||||
---
|
||||
|
||||
## Spec-derived constraints (no decision needed — locked by spec)
|
||||
|
||||
These are recorded for traceability; they are NOT clarify decisions, just restated spec locks that constrain the architecture.
|
||||
|
||||
- **Trigger.dev** for durable execution (spec §7 Q1) — bootstrapped in Wave A, tasks added in M3.
|
||||
- **WorkOS** for SSO/SAML + SCIM (spec §7 Q2) — Wave B.
|
||||
- **Vanta** for GRC (spec §7 Q3) — instrumentation in M3 only.
|
||||
- **Install script (curl|bash), apt fallback** (spec §7 Q4) — Wave D, modular functions.
|
||||
- **Fixed SSH whitelist, no customer extension in v0.1** (spec §7 Q5) — file + hook in Wave D, adapter in M2.
|
||||
- **PVEAuditor built-in role** (spec §7 Q6) — M2 (adapter), documented now.
|
||||
- **Gitea via SaaS-to-API exposure** (spec §7 Q7) — M2 (adapter).
|
||||
- **pgvector for v1.1 RAG** (spec §7 Q8) — not v0.1.
|
||||
|
||||
---
|
||||
|
||||
## Clarify summary
|
||||
|
||||
| ID | Decision | Confidence | Status |
|
||||
|----|----------|-----------|--------|
|
||||
| D-001 | OpenAI-compatible BYOM contract for M1 | 0.85 | approved |
|
||||
| D-002 | Relay Agent in Go | 0.90 | approved |
|
||||
| D-003 | AWS SM (prod) + local-encrypted (dev) behind interface | 0.85 | approved |
|
||||
| D-004 | Postgres append-only + hash-chain for M1 audit; S3 WORM in M3 | 0.80 | approved |
|
||||
| D-005 | Next.js (App Router) + TypeScript single SPA | 0.85 | approved |
|
||||
|
||||
All above-threshold (≥0.6). No escalations. Pipeline proceeds to RESEARCH.
|
||||
Reference in New Issue
Block a user