Files
mcr/PROGRESS.md
Kyle Isom 3314b7a618 Batch A: blob storage layer, MCIAS auth, OCI token endpoint
Phase 2 — internal/storage/:
Content-addressed blob storage with atomic writes via rename.
BlobWriter stages data in uploads dir with running SHA-256 hash,
commits by verifying digest then renaming to layers/sha256/<prefix>/<hex>.
Reader provides Open, Stat, Delete, Exists with digest validation.

Phase 3 — internal/auth/ + internal/server/:
MCIAS client with Login and ValidateToken, 30s SHA-256-keyed cache
with lazy eviction and injectable clock for testing. TLS 1.3 minimum
with optional custom CA cert.
Chi router with RequireAuth middleware (Bearer token extraction,
WWW-Authenticate header, OCI error format), token endpoint (Basic
auth → bearer exchange via MCIAS), and /v2/ version check handler.

52 tests passing (14 storage + 9 auth + 9 server + 20 existing).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:51:19 -07:00

9.6 KiB
Raw Blame History

MCR Development Progress

Reverse-chronological log of development work. Most recent entries first. See PROJECT_PLAN.md for the implementation roadmap and ARCHITECTURE.md for the full design specification.

Current State

Phase: Batch A complete (Phases 2 + 3), ready for Phase 4 (policy engine) Last updated: 2026-03-19

Completed

  • Phase 0: Project scaffolding (all 4 steps)
  • Phase 1: Configuration & database (all 3 steps)
  • Phase 2: Blob storage layer (all 2 steps)
  • Phase 3: MCIAS authentication (all 4 steps)
  • ARCHITECTURE.md — Full design specification (18 sections)
  • CLAUDE.md — AI development guidance
  • PROJECT_PLAN.md — Implementation plan (14 phases, 40+ steps)
  • PROGRESS.md — This file

Next Steps

  1. Phase 4: Policy engine (depends on Phase 3)
  2. After Phase 4, Batch B: Phase 5 (OCI pull) and Phase 8 (admin REST)

Log

2026-03-19 — Batch A: Phase 2 (blob storage) + Phase 3 (MCIAS auth)

Task: Implement content-addressed blob storage and MCIAS authentication with OCI token endpoint and auth middleware.

Changes:

Phase 2 — internal/storage/ (Steps 2.1 + 2.2):

  • storage.go: Store struct with layersPath/uploadsPath, New() constructor, digest validation (^sha256:[a-f0-9]{64}$), content-addressed path layout: <layers>/sha256/<first-2-hex>/<full-64-hex>
  • writer.go: BlobWriter wrapping *os.File + crypto/sha256 running hash via io.MultiWriter. StartUpload(uuid) creates temp file in uploads dir. Write() updates both file and hash. Commit(expectedDigest) finalizes hash, verifies digest, MkdirAll prefix dir, Rename atomically. Cancel() cleans up temp file. BytesWritten() returns offset.
  • reader.go: Open(digest) returns io.ReadCloser, Stat(digest) returns size, Delete(digest) removes blob + best-effort prefix dir cleanup, Exists(digest) returns bool. All validate digest format first.
  • errors.go: ErrBlobNotFound, ErrDigestMismatch, ErrInvalidDigest
  • No new dependencies (stdlib only)

Phase 3 — internal/auth/ (Steps 3.1) + internal/server/ (Steps 3.23.4):

  • auth/client.go: Client with NewClient(serverURL, caCert, serviceName, tags), TLS 1.3 minimum, optional custom CA cert, 10s HTTP timeout. Login() POSTs to MCIAS /v1/auth/login. ValidateToken() with SHA-256 cache keying and 30s TTL.
  • auth/claims.go: Claims struct (Subject, AccountType, Roles) with context helpers ContextWithClaims/ClaimsFromContext
  • auth/cache.go: validationCache with sync.RWMutex, lazy eviction, injectable now function for testing
  • auth/errors.go: ErrUnauthorized, ErrMCIASUnavailable
  • server/middleware.go: TokenValidator interface, RequireAuth middleware (Bearer token extraction, WWW-Authenticate header, OCI error format)
  • server/token.go: LoginClient interface, TokenHandler (Basic auth → bearer token exchange via MCIAS, RFC 3339 issued_at)
  • server/v2.go: V2Handler returning 200 {}
  • server/routes.go: NewRouter with chi: /v2/token (no auth), /v2/ (RequireAuth middleware)
  • server/ocierror.go: writeOCIError() helper for OCI error JSON format
  • New dependency: github.com/go-chi/chi/v5

Verification:

  • make all passes: vet clean, lint 0 issues, 52 tests passing (7 config + 13 db/audit + 14 storage + 9 auth + 9 server), all 3 binaries built
  • Storage tests: new store, digest validation (3 valid + 9 invalid), path layout, write+commit, digest mismatch rejection (temp cleanup verified), cancel cleanup, bytes written tracking, concurrent writes to different UUIDs, open after write, stat, exists, delete (verify gone), open not found, invalid digest format (covers Open/Stat/Delete/Exists)
  • Auth tests: cache put/get, TTL expiry with clock injection, concurrent cache access, login success/failure (httptest mock), validate success/revoked, cache hit (request counter), cache expiry (clock advance)
  • Server tests: RequireAuth valid/missing/invalid token, token handler success/invalid creds/missing auth, routes integration (authenticated /v2/, unauthenticated /v2/ → 401, token endpoint bypasses auth)

2026-03-19 — Phase 1: Configuration & database

Task: Implement TOML config loading with env overrides and validation, SQLite database with migrations, and audit log helpers.

Changes:

Step 1.1 — internal/config/:

  • config.go: Config struct matching ARCHITECTURE.md §10 (all 6 TOML sections: server, database, storage, mcias, web, log)
  • Parsed with go-toml/v2; env overrides via MCR_ prefix using reflection-based struct walker
  • Startup validation: 6 required fields checked (listen_addr, tls_cert, tls_key, database.path, storage.layers_path, mcias.server_url)
  • Same-filesystem check for layers_path/uploads_path via device ID comparison (walks to nearest existing parent if path doesn't exist yet)
  • Default values: read_timeout=30s, write_timeout=0, idle_timeout=120s, shutdown_timeout=60s, uploads_path derived from layers_path, log.level=info
  • device_linux.go: Linux-specific extractDeviceID using syscall.Stat_t
  • deploy/examples/mcr.toml: annotated example config

Step 1.2 — internal/db/:

  • db.go: Open(path) creates/opens SQLite via modernc.org/sqlite, sets pragmas (WAL, foreign_keys, busy_timeout=5000), chmod 0600
  • migrate.go: migration framework with schema_migrations tracking table; Migrate() applies pending migrations in transactions; SchemaVersion() reports current version
  • Migration 000001: repositories, manifests, tags, blobs, manifest_blobs, uploads — all tables, constraints, and indexes per ARCHITECTURE.md §8
  • Migration 000002: policy_rules, audit_log — tables and indexes per §8

Step 1.3 — internal/db/:

  • audit.go: WriteAuditEvent(eventType, actorID, repository, digest, ip, details) with JSON-serialized details map; ListAuditEvents(AuditFilter) with filtering by event_type, actor_id, repository, time range, and offset/limit pagination (default 50, descending by event_time)
  • AuditFilter struct with all filter fields
  • AuditEvent struct with JSON tags for API serialization

Lint fix:

  • .golangci.yaml: disabled fieldalignment analyzer in govet (micro- optimization that hurts struct readability; not a security/correctness concern)

Verification:

  • make all passes: vet clean, lint 0 issues, 20 tests passing (7 config + 13 db/audit), all 3 binaries built
  • Config tests: valid load, defaults applied, uploads_path default, 5 missing-required-field cases, env override (string + duration), same-filesystem check
  • DB tests: open+migrate, idempotent migrate, 9 tables verified, foreign key enforcement, tag cascade on manifest delete, manifest_blobs cascade (blob row preserved), WAL mode verified
  • Audit tests: write+list, filter by type, filter by actor, filter by repository, pagination (3 pages), null fields handled

2026-03-19 — Phase 0: Project scaffolding

Task: Set up Go module, build system, linter config, and binary entry points with cobra subcommands.

Changes:

  • go.mod: module git.wntrmute.dev/kyle/mcr, Go 1.25, cobra dependency
  • Directory skeleton: cmd/mcrsrv/, cmd/mcr-web/, cmd/mcrctl/, internal/, proto/mcr/v1/, gen/mcr/v1/, web/templates/, web/static/, deploy/docker/, deploy/examples/, deploy/scripts/, deploy/systemd/, docs/
  • .gitignore: binaries, srv/, *.db*, IDE/OS files
  • Makefile: standard targets (all, build, test, vet, lint, proto, proto-lint, clean, docker, devserver); all runs vet → lint → test → mcrsrv mcr-web mcrctl; CGO_ENABLED=0 on binary builds; version injection via -X main.version
  • .golangci.yaml: golangci-lint v2 config matching mc-proxy conventions; linters: errcheck, govet, ineffassign, unused, errorlint, gosec, staticcheck, revive; formatters: gofmt, goimports; gosec G101 excluded in test files
  • buf.yaml: protobuf linting (STANDARD) and breaking change detection (FILE)
  • cmd/mcrsrv/main.go: root command with server, init, snapshot subcommands (stubs returning "not implemented")
  • cmd/mcr-web/main.go: root command with server subcommand (stub)
  • cmd/mcrctl/main.go: root command with status, repo (list/delete), gc (trigger/status), policy (list/create/update/delete), audit (tail), snapshot subcommands (stubs)
  • All binaries accept --version flag

Verification:

  • make all passes: vet clean, lint 0 issues, test (no test files), all three binaries built successfully
  • ./mcrsrv --versionmcrsrv version 3695581
  • ./mcr-web --versionmcr-web version 3695581
  • All stubs return "not implemented" error as expected
  • make clean removes binaries

2026-03-19 — Project planning

Task: Create design documents and implementation plan.

Changes:

  • README.md: Existing one-line description
  • ARCHITECTURE.md: Full design specification covering OCI Distribution Spec compliance, MCIAS authentication, policy engine, storage design, API surface (OCI + admin REST + gRPC), database schema, garbage collection, configuration, web UI, CLI tools, deployment, security model
  • CLAUDE.md: Development guidance for AI-assisted implementation
  • PROJECT_PLAN.md: 14-phase implementation plan with discrete steps, acceptance criteria, dependency graph, and batchable work identification
  • PROGRESS.md: This progress tracker

Notes:

  • No code written yet. All files are documentation/planning.
  • ARCHITECTURE.md reviewed and corrected for: GC algorithm crash safety, policy glob semantics, tag FK cascade, OCI error format, API sync violations, timeout configuration, backup considerations, and other consistency issues.