Phases 11, 12: mcrctl CLI tool and mcr-web UI

Phase 11 implements the admin CLI with dual REST/gRPC transport,
global flags (--server, --grpc, --token, --ca-cert, --json), and
all commands: status, repo list/delete, policy CRUD, audit tail,
gc trigger/status/reconcile, and snapshot.

Phase 12 implements the HTMX web UI with chi router, session-based
auth (HttpOnly/Secure/SameSite=Strict cookies), CSRF protection
(HMAC-SHA256 signed double-submit), and pages for dashboard,
repositories, manifest detail, policy management, and audit log.

Security: CSRF via signed double-submit cookie, session cookies
with HttpOnly/Secure/SameSite=Strict, TLS 1.3 minimum on all
connections, form body size limits via http.MaxBytesReader.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 10:14:38 -07:00
parent 185b68ff6d
commit 593da3975d
23 changed files with 3737 additions and 66 deletions

View File

@@ -6,7 +6,7 @@ See `PROJECT_PLAN.md` for the implementation roadmap and
## Current State
**Phase:** 10 complete, ready for Phase 11
**Phase:** 12 complete, ready for Phase 13
**Last updated:** 2026-03-19
### Completed
@@ -22,6 +22,8 @@ See `PROJECT_PLAN.md` for the implementation roadmap and
- Phase 8: Admin REST API (all 5 steps)
- Phase 9: Garbage collection (all 2 steps)
- Phase 10: gRPC admin API (all 4 steps)
- Phase 11: CLI tool (all 3 steps)
- Phase 12: Web UI (all 5 steps)
- `ARCHITECTURE.md` — Full design specification (18 sections)
- `CLAUDE.md` — AI development guidance
- `PROJECT_PLAN.md` — Implementation plan (14 phases, 40+ steps)
@@ -29,7 +31,114 @@ See `PROJECT_PLAN.md` for the implementation roadmap and
### Next Steps
1. Phase 11 (CLI tool) and Phase 12 (web UI)
1. Phase 13 (deployment artifacts)
---
### 2026-03-19 — Batch C: Phase 11 (CLI tool) + Phase 12 (Web UI)
**Task:** Implement the admin CLI and HTMX-based web UI — the two
remaining user-facing layers. Both depend on Phase 10 (gRPC) but not
on each other; implemented in parallel.
**Changes:**
Phase 11 — `cmd/mcrctl/` (Steps 11.111.3):
Step 11.1 — Client and connection setup:
- `client.go`: `apiClient` struct wrapping both `*http.Client` (REST)
and gRPC service clients (Registry, Policy, Audit, Admin); `newClient()`
builds from flags; TLS 1.3 minimum with optional custom CA cert;
gRPC dial uses `grpc.ForceCodecV2(mcrv1.JSONCodec{})` for JSON codec;
`restDo()` helper with `Authorization: Bearer` header and JSON error
parsing; transport auto-selected based on `--grpc` flag
Step 11.2 — Status and repository commands:
- `main.go`: global persistent flags `--server`, `--grpc`, `--token`
(fallback `MCR_TOKEN`), `--ca-cert`, `--json`; `PersistentPreRunE`
resolves token and creates client; `status` command (gRPC + REST);
`repo list` with table/JSON output; `repo delete` with confirmation
prompt
- `output.go`: `formatSize()` (B/KB/MB/GB/TB), `printJSON()` (indented),
`printTable()` via `text/tabwriter`
Step 11.3 — Policy, audit, GC, and snapshot commands:
- `main.go`: `policy list|create|update|delete` (full CRUD, `--rule`
flag for JSON body, confirmation on delete); `audit tail` with
`--n` and `--event-type` flags; `gc` with `--reconcile` flag;
`gc status`; `snapshot`; all commands support both REST and gRPC
- `client_test.go`: 10 tests covering formatSize, printJSON, printTable,
token resolution from env/flag, newClient REST mode, CA cert error
handling, restDo success/error/POST paths
Phase 12 — `cmd/mcr-web/` + `internal/webserver/` + `web/` (Steps 12.112.5):
Step 12.1 — Web server scaffolding:
- `cmd/mcr-web/main.go`: reads `[web]` config section, creates gRPC
connection with TLS 1.3 and JSON codec, creates MCIAS auth client for
login, generates random 32-byte CSRF key, creates webserver, starts
HTTPS with TLS 1.3, graceful shutdown on SIGINT/SIGTERM
- `internal/webserver/server.go`: `Server` struct with chi router,
gRPC service clients, CSRF key, login function; `New()` constructor;
chi middleware (Recoverer, RequestID, RealIP); routes for all pages;
session-protected route groups; static file serving from embedded FS
- `web/embed.go`: `//go:embed templates static` directive
- `web/static/style.css`: minimal clean CSS (system fonts, 1200px
container, table styling, form styling, nav bar, stat cards, badges,
pagination, responsive breakpoints)
Step 12.2 — Login and authentication:
- `internal/webserver/auth.go`: session middleware (checks `mcr_session`
cookie, redirects to `/login` if absent); login page (GET renders
form with CSRF token); login submit (POST validates CSRF, calls
`loginFn`, sets session cookie HttpOnly/Secure/SameSite=Strict);
logout (clears cookie, redirects); CSRF via signed double-submit
cookie (HMAC-SHA256)
- `web/templates/login.html`: centered login form with CSRF hidden field
Step 12.3 — Dashboard and repository browsing:
- `internal/webserver/handlers.go`: `handleDashboard()` (repo count,
total size, recent audit events via gRPC); `handleRepositories()`
(list table); `handleRepositoryDetail()` (tags, manifests, repo
name with `/` support); `handleManifestDetail()` (manifest info
by digest)
- `internal/webserver/templates.go`: template loading from embedded FS
with layout-page composition, function map (formatSize, formatTime,
truncate, joinStrings), render helper
- `web/templates/layout.html`: HTML5 base with nav bar, htmx CDN
- `web/templates/dashboard.html`: stats cards + recent activity table
- `web/templates/repositories.html`: repo list table
- `web/templates/repository_detail.html`: tags + manifests tables
- `web/templates/manifest_detail.html`: digest, media type, size
Step 12.4 — Policy management (admin only):
- `internal/webserver/handlers.go`: `handlePolicies()` (list rules
with CSRF token); `handleCreatePolicy()` (form with body limit,
CSRF validation); `handleTogglePolicy()` (get+toggle enabled via
UpdatePolicyRule with field mask); `handleDeletePolicy()` (CSRF +
delete); PermissionDenied → "Access denied"
- `web/templates/policies.html`: create form + rules table with
toggle/delete actions
Step 12.5 — Audit log viewer (admin only):
- `internal/webserver/handlers.go`: `handleAudit()` with pagination
(fetch N+1 for next-page detection), filters (event type, repository,
date range), URL builder for pagination links
- `web/templates/audit.html`: filter form + paginated event table
**Verification:**
- `make all` passes: vet clean, lint 0 issues, all tests passing,
all 3 binaries built
- CLI tests (10 new): formatSize (5 values: B through TB), printJSON
output correctness, printTable header and row rendering, token from
env var, token flag overrides env, newClient REST mode, CA cert
errors (missing file, invalid PEM), restDo success with auth header,
restDo error response parsing, restDo POST with body
- Web UI tests (15 new): login page renders, invalid credentials error,
CSRF token validation, dashboard requires session (redirect),
dashboard with session, repositories page, repository detail,
logout (cookie clearing), policies page, audit page, static files,
formatSize, formatTime, truncate, login success sets cookie
---