Client-side purge that keeps the last N tags per repository (excluding
latest) and deletes older manifests. Uses existing MCR APIs — no new
server RPCs needed.
Server-side: added updated_at to TagInfo struct and GetRepositoryDetail
query so tags can be sorted by recency.
Usage: mcrctl purge --keep 3 --dry-run --gc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The web UI now validates the MCIAS token after login and rejects
accounts with the guest role before setting the session cookie.
This is defense-in-depth alongside the env:restricted MCIAS tag.
The webserver.New() constructor takes a new ValidateFunc parameter
that inspects token roles post-authentication. MCIAS login does not
return roles, so this requires an extra ValidateToken round-trip at
login time (result is cached for 30s).
Security: guest role accounts are denied web UI access
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace MCR's custom auth, admin, and logging interceptors with the
shared mcdsl grpcserver package. This eliminates ~110 lines of
interceptor code and uses the same method-map auth pattern used by
metacrypt.
Key changes:
- server.go: delegate to mcdslgrpc.New() for TLS, logging, and auth
- interceptors.go: replaced with MethodMap definition (public, auth-required, admin-required)
- Handler files: switch from auth.ClaimsFromContext to mcdslauth.TokenInfoFromContext
- auth/client.go: add Authenticator() accessor for the underlying mcdsl authenticator
- Tests: use mock MCIAS HTTP server instead of fakeValidator interface
- Vendor: add mcdsl/grpcserver to vendor directory
ListRepositories and GetRepository are now explicitly auth-required
(not admin-required), matching the REST API. Previously they were
implicitly auth-required by not being in the bypass or admin maps.
Security: method map uses default-deny -- unmapped RPCs are rejected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Every 500 response in the OCI package silently discarded the actual
error, making production debugging impossible. Add slog.Error before
each 500 response with the error and relevant context (repo, digest,
tag, uuid). Add slog.Info for state-mutating successes (manifest push,
blob upload complete, deletions).
Logger is injected into the OCI Handler via constructor, falling back
to slog.Default() if nil.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NewRouter now accepts an optional OCI handler to mount inside the
authenticated /v2 route group, avoiding chi's Mount conflict on
an existing path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server command loads config, opens and migrates DB, creates auth
client, blob storage, GC collector, policy engine, OCI handler,
mounts HTTP routes (OCI + admin REST), starts optional gRPC server,
and handles graceful shutdown on SIGINT/SIGTERM.
Status command performs a health check against the /v1/health endpoint
with optional CA cert for TLS verification.
Snapshot command performs VACUUM INTO to /srv/mcr/backups/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>