- db.Open: delegate to mcdsl/db.Open
- db.Migrate: rewrite migrations as mcdsl/db.Migration SQL strings,
delegate to mcdsl/db.Migrate; keep SchemaVersion via mcdsl
- auth: thin shim wrapping mcdsl/auth.Authenticator, keeps Claims
type (with Subject, AccountType, Roles) for policy engine compat;
delete cache.go (handled by mcdsl/auth); add ErrForbidden
- config: embed mcdsl/config.Base for standard sections (Server with
Duration fields, Database, MCIAS, Log); keep StorageConfig and
WebConfig as MCR-specific; use mcdsl/config.Load[T] + Validator
- WriteTimeout now defaults to 30s (mcdsl default, was 0)
- All existing tests pass (auth tests rewritten for new shim API,
cache expiry test removed — caching tested in mcdsl)
- Net -464 lines
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GC engine (internal/gc/): Collector.Run() implements the two-phase
algorithm — Phase 1 finds unreferenced blobs and deletes DB rows in
a single transaction, Phase 2 deletes blob files from storage.
Registry-wide mutex blocks concurrent GC runs. Collector.Reconcile()
scans filesystem for orphaned files with no DB row (crash recovery).
Wired into admin_gc.go: POST /v1/gc now launches the real collector
in a goroutine with gc_started/gc_completed audit events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Manifest delete (DELETE /v2/<name>/manifests/<digest>): rejects tag
references with 405 UNSUPPORTED per OCI spec, cascades to tags and
manifest_blobs via ON DELETE CASCADE, returns 202 Accepted.
Blob delete (DELETE /v2/<name>/blobs/<digest>): removes manifest_blobs
associations only — blob row and file are preserved for GC to handle,
since other repos may reference the same content-addressed blob.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
internal/policy/:
Priority-based policy engine per ARCHITECTURE.md §4. Stateless
Evaluate() sorts rules by priority, collects all matches, deny-wins
over allow, default-deny if no match. Rule matching: all populated
fields ANDed, empty fields are wildcards, repository glob via
path.Match. Built-in defaults: admin wildcard (all actions), human
user content access (pull/push/delete/catalog), version check
(always accessible). Engine wrapper with sync.RWMutex-protected
cache, SetRules merges with defaults, Reload loads from RuleStore.
internal/db/:
LoadEnabledPolicyRules() parses rule_json column from policy_rules
table into []policy.Rule, filtered by enabled=1, ordered by priority.
internal/server/:
RequirePolicy middleware extracts claims from context, repo from chi
URL param, evaluates policy, returns OCI DENIED (403) on deny with
optional audit callback.
69 tests passing across all packages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>