# Changelog

All notable changes to ForgeBucket are documented here.

Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---

## [Unreleased]

### Planned — Phase 5 (AI Diagnostics + Deployment Promotions + Rollback Visualization)
- AI-powered pipeline failure diagnostics
- Deployment promotion workflows (manual + automated)
- Rollback visualization and timeline

---

## [1.0.0] — 2026-05-13

Phase 4 complete. SBOM generation, secret scanning, dependency vulnerability scanning, signed artifacts, and OCI registry are operational.

### Added — SBOM Generation (`internal/domain/sbom/`)
- **`Generator`** — subscribes to `pipeline.completed` events and auto-generates CycloneDX 1.4 SBOM documents for every successful pipeline run; also supports on-demand generation via `GenerateOnDemand`
- **6 manifest parsers**: `go.mod`, `package.json`, `requirements.txt`, `Cargo.toml`, `Gemfile.lock`, `pom.xml` — lightweight line-scanning, no external parser dependencies
- **API endpoints** — `GET /sbom`, `GET /sbom/document`, `GET /runs/{runID}/sbom`, `GET /runs/{runID}/sbom/document`, `POST /sbom/generate?ref=&runID=`
- **Database** — migration `016_sbom` adds `SBOMReport` model with CycloneDX document body
- Automatic generation on pipeline completion now also fires directly from the orchestrator (not solely via NATS), ensuring SBOMs are generated even when NATS is unavailable

### Added — Secret Scanning (`internal/domain/scanning/`)
- **`Scanner`** — subscribes to `push.received` events, scans git diffs against 15 regex patterns for high/medium severity secrets
- **Secret patterns**: AWS keys, GitHub/GitLab tokens, generic API keys, Bearer tokens, Slack tokens, Google API keys, Google service accounts, SSH private keys, JWTs, NPM tokens, PostgreSQL/Redis connection strings, generic passwords
- **API endpoints** — `GET /secrets/leaks`, `POST /secrets/leaks/{leakID}/dismiss` (repo-scoped), `GET /api/v1/secrets/leaks` (global admin)
- **Database** — migration `018_scanning` adds `SecretLeak` model

### Added — Vulnerability Scanning (`internal/domain/vulnscan/`)
- **`Scanner`** — triggers on-demand scans against the OSV API (`api.osv.dev/v1`); supports scanning by PURL or by fetching the latest SBOM and scanning all components
- **OSV client** — HTTP client with 30-second timeout, queries OSV database for CVEs by PURL or ecosystem+name, extracts CVSS scores and fixed version ranges
- **API endpoints** — `GET /vulnerabilities`, `POST /vulnerabilities/scan`, `POST /vulnerabilities/{findingID}/dismiss` (repo-scoped), `GET /api/v1/vulnerabilities` (global admin)
- **Database** — migration `019_vulnscan` adds `VulnerabilityFinding` model
- Findings deduplicated by `(vuln_id, purl, repo_id)`

### Added — Artifact Signing (`internal/domain/signing/`)
- **`KeyStore`** — ECDSA P-256 signing and verification; produces self-verifying `Bundle` carrying payload, signature (ASN.1 DER), and public key PEM
- `Sign(artifactID, name, rawContent)` — computes SHA-256 digest, signs, returns signed `Bundle` with key ID fingerprint
- `Verify(bundleJSON)` — extracts public key from bundle, verifies ECDSA signature, returns `VerifyResult` with key-matching check
- `Generate()` — creates ephemeral ECDSA P-256 key when `ARTIFACT_SIGNING_KEY` env var is unset (logs warning; signatures lost on restart unless persisted)
- **API endpoints** — `GET /artifacts/{artifactID}/signature`, `GET /artifacts/{artifactID}/verify`
- **Database** — migration `015_signing` adds `ArtifactSignature` model

### Added — OCI Registry (`internal/domain/oci/`)
- **`Registry`** — content-addressable on-disk blob store implementing OCI Distribution Spec v1.1
- Storage layout: `{root}/blobs/sha256/<hex>` for blobs, `{root}/uploads/<uuid>` for in-progress uploads
- Full upload session lifecycle: start (POST), append chunk (PATCH), finalize with digest verification (PUT), cancel (DELETE), offset query (GET)
- 13 OCI distribution error codes defined (`ErrBlobUnknown`, `ErrDigestInvalid`, `ErrManifestInvalid`, etc.)
- **API handlers** (`internal/api/handlers/oci.go`, 525 lines) — full `/v2/{name}/{kind}/{ref}` routing: manifest push/get/delete, blob HEAD/get/delete, tag listing, chunked upload
- **Database** — migration `017_oci` adds `OCIRepository`, `OCIManifest`, `OCITag`, `OCIBlob`, `OCIUpload` models
- Registry is consumed by standard OCI tools (Docker, Podman, Skopeo, containerd)

### Added — Unified Security Page (`/repos/:owner/:repo/security`)
- **`RepoSecurityPage`** — single-page view combining SBOM status, secret leak detection, and vulnerability findings
- SBOM section: displays existing SBOM metadata with download button, or "Generate SBOM" form with branch/SHA input
- Secret Leaks section: lists leaks with severity badge, pattern name, commit SHA, ref, match sample, dismiss button
- Vulnerabilities section: lists findings with CVSS severity (CRITICAL/HIGH/MEDIUM/LOW), vuln ID, score, summary, PURL, version, fix suggestion, dismiss button; "Scan now" trigger
- **Route** added in `App.tsx`, nav link added in `RepoPage.tsx` tab bar

### Added — Pipeline Run SBOM Integration
- `PipelineRunPage` shows per-run SBOM section: metadata (components, SHA, generation time) + download button
- "Generate SBOM" button for completed/failed runs that lack one
- `useRunSBOM` / `useGenerateSBOM` hooks in `frontend/src/api/queries/sbom.ts`
- 404 from `useRunSBOM` handled gracefully (returns `null` instead of throwing)

### Added — Database Models
- Migration `016_sbom` — `SBOMReport` (repoId, runId, sha, format, componentCount, bomDocument, generatedAt)
- Migration `017_oci` — `OCIRepository`, `OCIManifest`, `OCITag`, `OCIBlob`, `OCIUpload`
- Migration `018_scanning` — `SecretLeak` (repoId, commitSha, ref, patternName, description, severity, matchSample, dismissed, dismissedBy, dismissedAt, detectedAt)
- Migration `019_vulnscan` — `VulnerabilityFinding` (repoId, vulnId, purl, version, summary, details, cvssScore, fixedVersion, dismissed, dismissedBy, dismissedAt, detectedAt)
- Migration `020_forgefed` — repository + pull request column updates

### Fixed
- SBOM per-run download endpoint (`/runs/{runID}/sbom/document`) was registered at the wrong router nesting level, causing a route conflict with the `GetLatestDocument` handler. Moved into the correct `/runs/{runID}` route block.
- `username` context key extraction in scanning and vulnerability handlers changed from raw string `"user"` to typed `middleware.ContextKeyUsername`
- Nil-safe `Needs` marshalling in orchestrator job creation
- Nil-safe findings response in vulnerability scan API
- `GenerateOnDemand` SBOM cache key now includes `runID` to prevent per-run generation from being shadowed by prior on-demand generation

---

## [0.9.0] — 2026-05-12

Phase 3F complete. ForgeBucket is now a first-class ActivityPub node — interoperable with Mastodon, Forgejo, and any fediverse server.

### Added — ActivityPub Federation (`internal/domain/federation/`)

- **`GET /.well-known/webfinger`** — resolves `acct:user@domain` to the actor URL; returns `application/jrd+json`
- **`GET /users/{username}`** — returns a JSON-LD actor document (`Person` type) including public key object for HTTP signature verification
- **`POST /users/{username}/inbox`** — receives and dispatches inbound ActivityPub activities; HTTP signature verification enforced in production (skipped in `DEBUG=true` mode for local testing)
- **`GET /users/{username}/outbox`** — serves an `OrderedCollection` (summary on page 0, paginated `OrderedCollectionPage` on page ≥ 1, 20 activities per page)
- **`GET /users/{username}/followers`** — stub `OrderedCollection` (zero items; social graph in Phase 4)
- **`GET /users/{username}/following`** — stub `OrderedCollection`

### Added — HTTP Signatures (`internal/domain/federation/signatures.go`)
- `Sign(req, keyID, privateKeyPEM)` — signs outgoing HTTP requests with RSA-SHA256; covers `(request-target)`, `host`, and `date` headers
- `Verify(r, db, instanceURL)` — parses `Signature` header, resolves sender's public key (local `FederationActor` first, then network fetch via `FetchActor`), verifies RSA-SHA256 digest

### Added — Actor Lifecycle (`internal/domain/federation/actor.go`)
- `GetOrCreate` — lazily creates a `FederationActor` for a local user; generates a fresh RSA-2048 key pair and derives `InboxURL`, `OutboxURL`, `APID` from `INSTANCE_URL`; stable across requests
- `ActorJSON` — returns the JSON-LD document shape expected by all ActivityPub clients
- `APID(instanceURL, username)` — canonical `{instanceURL}/users/{username}` helper

### Added — Follow / Accept Flow (`internal/domain/federation/inbox.go`)
- Incoming `Follow` activities are auto-accepted: remote actor is fetched (or retrieved from cache), an `Accept` activity is signed and delivered to their inbox asynchronously
- Both the inbound `Follow` and outbound `Accept` are persisted to `FederationActivity` for audit

### Added — Remote Actor Cache (`internal/domain/federation/remote.go`)
- `FetchActor` — HTTP GET with `Accept: application/activity+json`, extracts inbox URL and public key PEM, stores in `RemoteActor` table to avoid repeated fetches
- `DeliverActivity` — marshals activity JSON, signs the request, POSTs to recipient inbox with 15-second timeout

### Added — Database Models (migration `014_federation`)
- `FederationActivity` — append-only log of all inbound and outbound activities: `ActorAPID`, `Type`, `ObjectJSON`, `Direction` (inbound/outbound), `RemoteActor`, `Published`
- `RemoteActor` — cache for remote actor documents: `APID` (unique), `InboxURL`, `PublicKey`, `FetchedAt`

---

## [0.8.0] — 2026-05-12

Phase 3E complete. Prometheus metrics, structured health checks, and per-repo operational health are operational.

### Added — Prometheus Metrics (`internal/observability/`)
- `GET /metrics` — Prometheus text format endpoint (standard root-level path for k8s/Prometheus scraping)
- `GET /health` — upgraded from static `{"status":"ok"}` to a structured liveness response:
  `{"status":"healthy","checks":{"database":"ok","nats":"ok"},"version":"0.8.0"}`
  Returns HTTP 503 when any dependency is degraded
- `internal/observability/metrics.go` — metric definitions:
  - `forgebucket_http_requests_total{method,path,status}` — counter
  - `forgebucket_http_request_duration_seconds{method,path}` — histogram (Prometheus default buckets)
  - `forgebucket_pipeline_runs_total{status}` — counter (succeeded/failed/cancelled), pre-initialized to 0
  - `forgebucket_deployments_total{status}` — counter (pending/success/failure/cancelled), pre-initialized to 0
  - `forgebucket_active_pipeline_runs` — gauge (in-flight runs)
- `internal/observability/health.go` — `Check(db, bus)` pings PostgreSQL and calls `bus.Healthy()`
- HTTP instrumentation middleware inserted after `Recoverer`, before `CORS` — records every request
- Path normalization prevents label cardinality explosion: `/repos/alice/myrepo/runs/42` →
  `/api/v1/repos/:owner/:repo/runs/:id`
- NATS metric watcher subscribes to `pipeline.>` and `deployment.>` and increments counters

### Added — Per-Repo Operational Health (`GET /api/v1/repos/{owner}/{repo}/health`)
- Returns a JSON summary for the repo page operational header:
  - `ciPassRate7d` — fraction of pipeline runs that succeeded in the last 7 days
  - `totalRuns7d` — total run count in the last 7 days
  - `latestRun` — most recent `PipelineRun` record
  - `latestDeployments` — one entry per environment showing latest deploy (envName, status, sha, finishedAt)
  - `openDriftCount` — GitOpsConfigs in `drifted` state
  - `openPRCount` — open pull request count

### Added — EventBus `Healthy() bool`
- Added to the `EventBus` interface; `NATSBus` returns `nc.IsConnected()`; `NoOpBus` returns `true`

### Changed — Middleware chain
- `observability.Middleware()` added between `Recoverer` and `CORS` (applies to all requests including `/health` and `/metrics`)

---

## [0.7.0] — 2026-05-12

Phase 3D complete. Git is now the source of truth for environment deployment state.

### Added — GitOps Controller (`internal/domain/gitops/`)
- `controller.go` — starts as a background goroutine; subscribes to `push.received`,
  `deployment.succeeded`, `deployment.failed`; runs a periodic reconciliation ticker
  (interval configurable via `GITOPS_RECONCILE_INTERVAL`); recovers stale `syncing`
  configs to `drifted` on startup
- `drift.go` — `CheckDrift` calls `git rev-parse` via the existing git domain wrapper;
  `handlePush` queries all GitOpsConfigs matching the pushed branch and evaluates drift;
  `periodicCheck` iterates configs whose `SyncInterval` has elapsed; publishes
  `environment.drift_detected` when drift is found
- `reconciler.go` — `TriggerSync` creates a `Deployment` record and publishes
  `deployment.started` (same lifecycle path as manual deployments, `TriggeredBy="gitops"`);
  `handleDeploymentSucceeded` resolves open drift events and marks config `synced` for
  both GitOps and manual deployments; `handleDeploymentFailed` reverts to `drifted`

### Added — GitOps HTTP API (`internal/api/handlers/gitops.go`)
All routes live under `/api/v1/repos/{owner}/{repo}/environments/{envName}/gitops/`:
- `GET    /gitops` — current GitOpsConfig or 404 if not configured
- `PUT    /gitops` — idempotent upsert (branch, autoSync, syncInterval)
- `DELETE /gitops` — remove config without deleting deployments
- `POST   /gitops/sync` — manual reconciliation trigger; creates deployment record
- `GET    /gitops/drift` — current sync status: syncStatus, desiredSha, actualSha, isDrifted
- `GET    /gitops/drift/history` — paginated drift event log (newest first)
- `POST   /gitops/drift/{driftID}/acknowledge` — acknowledge without syncing

### Added — Database Models (migration `013_gitops`)
- `GitOpsConfig` — links environment to a branch; tracks `DesiredSHA`, `ActualSHA`,
  `SyncStatus` (`unknown/synced/drifted/syncing`), `AutoSync`, `SyncInterval`,
  `LastCheckedAt`
- `GitOpsDriftEvent` — append-only drift record: `DesiredSHA`, `ActualSHA`,
  `SyncStatus` (`drifted/synced/acknowledged`), `DetectedAt`, `ResolvedAt`

### Added — Supporting Changes
- `git.RevParse(repoPath, ref)` — new function in `internal/domain/git/binary.go`
  used by `CheckDrift` to resolve branch HEAD SHA
- `events.DeploymentEvent` + `events.DriftEvent` types added to `internal/events/types.go`
- `EnvironmentHandler.publishDeployEvent` updated to use shared `events.DeploymentEvent`
  so the GitOps controller can unmarshal deployment lifecycle events correctly
- `GITOPS_RECONCILE_INTERVAL` env var (default `300`s); `0` disables the periodic ticker
- `ArtifactRoot` config field + `ARTIFACT_ROOT` env var

---

## [0.6.0] — 2026-05-12

Phase 3C complete. Multi-tenant workspaces and a full secret management hierarchy operational.

### Added — Workspaces
- `Workspace` model (migration `011`): globally unique handle, display name, description, avatarUrl
- `WorkspaceMember` model: owner/admin/member roles per workspace
- Repository `workspace_id` column (optional; null = personal repo)
- Full workspace CRUD API: `GET/POST /api/v1/workspaces`, `GET/PATCH/DELETE /api/v1/workspaces/{handle}`
- Workspace member management: list, add, update role, remove
- `GET /api/v1/workspaces/{handle}/repos` — repos in workspace
- Workspace frontend: WorkspacesPage, WorkspacePage, workspace switcher in sidebar header
- Workspace owner selector in repo create flow

### Added — Secret Management (`internal/api/handlers/secret.go`)
- `Secret` model (migration `012`): `Scope` (global/workspace/repo/env), `ScopeID`, `Name`,
  `EncryptedValue` (AES-256-GCM, never returned by API)
- Unique constraint on (scope, scope_id, name)
- CRUD at all scope levels:
  - `GET/POST/DELETE /api/v1/admin/secrets` (global, admin-only)
  - `GET/POST/DELETE /api/v1/workspaces/{handle}/secrets` (workspace-scoped)
  - `GET/POST/DELETE /api/v1/repos/{owner}/{repo}/secrets` (repo-scoped)
  - `GET/POST/DELETE /api/v1/repos/{owner}/{repo}/environments/{envName}/secrets` (env-scoped)
- `ResolveSecretsForRun(db, repoID, workspaceID, envID, sessionSecret)` — hierarchy
  resolution for CI executor: Env > Repo > Workspace > Global
- CI executor updated to inject resolved secrets as Docker `--env` flags
- RepoSecretsPage — write-only UI, values never displayed after creation
- Sidebar "Secrets" nav item in repo context

---

## [0.5.0] — 2026-05-11

Phases 3A and 3B complete. Environments, deployments, and the operational timeline are operational.

### Added — Environments + Deployments (Phase 3A)
- `Environment` model (migration `010`): repoId, name, URL, protectionRules (JSON)
- `Deployment` model: envId, repoId, sha, ref, status lifecycle
  (`pending → in_progress → success/failure/cancelled`), triggeredBy, description, runId link
- CRUD API for environments: `GET/POST /environments`, `GET/PATCH/DELETE /environments/{envName}`
- Deployment API: `GET/POST /environments/{envName}/deployments`,
  `PATCH /environments/{envName}/deployments/{id}/status`
- NATS events published on status transitions: `deployment.started`, `deployment.succeeded`,
  `deployment.failed`
- `EnvironmentsPage` — environment cards each showing latest deployment status, SHA, actor,
  and time since deploy; deployment history per env
- Sidebar "Environments" nav item in repo context
- Repo page deployment status badges (latest deploy per env at a glance)

### Added — Unified Operational Timeline (Phase 3B)
- `GET /api/v1/repos/{owner}/{repo}/timeline` — merged chronological feed of commits,
  pipeline runs, and deployments; default 60 events, max 200
- `RepoTimelinePage` at `/repos/:owner/:repo/timeline` — vertical event feed with type
  filter tabs (all / commits / runs / deployments)
- Sidebar "Timeline" nav item between Environments and Settings
- Answers "what changed before things broke?" without navigating between separate pages

---

## [0.4.0] — 2026-05-11

Phase 2C complete. CI results are legible in the UI; the dashboard is an operational command center.

### Added — Pipeline Visualization
- `PipelinesPage` — cross-repo pipeline runs feed with status filter tabs (all / running / failed / succeeded)
- `RepoPipelinesPage` — repo-scoped runs list at `/repos/:owner/:repo/pipelines`
- `PipelineRunPage` — run detail with topological DAG visualization using real `PipelineJob[]` +
  `needs` graph; step log viewer (collapsible per step, ANSI color, auto-scroll with lock toggle)
- `PipelineWaterfall` — rewritten to accept live job data instead of static mock stages
- `GET /api/v1/pipelines/runs` — cross-repo recent runs for the dashboard

### Added — Dashboard CI Command Center
- Dashboard CI widget replaced "coming soon" with live recent pipeline runs
- Dashboard `recentRuns[]` field added to the `/api/v1/dashboard` response

### Added — Command Palette Wiring
- Pipeline run results surfaced in command palette results
- "Pipelines" quick-nav action

---

## [0.3.0] — 2026-05-11

Phase 2B complete. Full CI/CD execution backend operational.

### Added — CI Orchestrator (`internal/domain/ci/`)
- DAG-based pipeline orchestrator (`orchestrator.go`): subscribes to NATS `push.received`,
  parses `.forgebucket/workflows/*.yml`, creates `PipelineRun/Job/Step` records, advances
  DAG on `job.completed/failed`, recovers stale runs on startup
- Docker executor (`executor.go`): steps run in isolated containers (`docker run --rm`),
  logs stream to DB and NATS via `pipeline.log`, workspace extracted via `git archive`
- Runner manager (`runner_manager.go`): semaphore-limited (default 4 concurrent),
  subscribes to `job.queued`, skips gracefully if Docker is unavailable
- DAG engine (`dag.go`): `TopoSort`, `ReadyJobs`
- Workflow parser (`parser.go`): `.forgebucket/workflows/*.yml` from git ref,
  `MatchesPushTrigger` with glob branch patterns; `StringOrSlice` YAML unmarshaler

### Added — CI API Handlers
- `GET  /api/v1/repos/:owner/:repo/pipelines` — pipeline definitions
- `GET  /api/v1/repos/:owner/:repo/runs` — pipeline runs (newest first)
- `GET  /api/v1/repos/:owner/:repo/runs/:runID` — run detail with job + step tree
- `POST /api/v1/repos/:owner/:repo/runs/:runID/cancel`
- `POST /api/v1/repos/:owner/:repo/runs/:runID/jobs/:jobID/retry`
- `GET  /api/v1/repos/:owner/:repo/runs/:runID/jobs/:jobID/logs` — step log chunks
- `GET/POST /api/v1/repos/:owner/:repo/runs/:runID/artifacts`
- `GET  /api/v1/repos/:owner/:repo/artifacts/:artifactID/download` — path-traversal guarded
- `GET/POST /api/v1/admin/runners` — runner list + registration (admin-only, bcrypt token)

### Added — Database Models (migration `009_ci`)
- `Pipeline`, `PipelineRun`, `PipelineJob`, `PipelineStep`, `PipelineStepLog`
- `Runner` (name, labels, status, tokenHash, lastSeenAt)
- `Artifact` (runId, repoId, name, storagePath, size, contentType)

### Changed — Git HTTP handler
- `parseAndCheckBody` replaces `checkProtectionsFromBody` — now also returns parsed
  `refUpdate` structs for publishing `push.received` after each successful receive-pack

---

## [0.2.0] — 2026-05-11

Phase 2A complete. Real-time event infrastructure and audit log operational.

### Added — NATS Event Bus (`internal/events/`)
- `EventBus` interface: `Publish`, `Subscribe`, `Close`
- `NATSBus`: NATS-backed with auto-reconnect; `NoOpBus` fallback when `NATS_URL` unset
- `New(url)` factory: returns `NATSBus` or `NoOpBus`
- 40+ event subjects in `subjects.go` covering repo, push, PR, issue, pipeline, job,
  deployment, environment, and audit namespaces

### Added — WebSocket Hub
- `GET /ws` — NATS wildcard subscription (`>`) fans all events to connected clients as JSON
- `{ subject, payload }` envelope format
- Goroutine per client with buffered send channel (64 events); slow clients drop events

### Added — Audit Log (migration `008_audit_log`)
- `AuditLog` model: actorId, actorName, method, path, statusCode, ipAddress, userAgent
- Middleware records every POST/PUT/PATCH/DELETE in the protected route group
- Writes DB row + publishes `audit.event` asynchronously (never blocks the response)
- `GET /api/v1/audit` — paginated, filterable by actor/method/since (admin-only)

---

## [0.1.0] — 2026-05-11

Initial development milestone. Core Git hosting, collaboration, and frontend SPA functional.

### Added — Authentication & Security
- User registration and login with secure session cookies
- CSRF protection via double-submit cookie pattern (`X-CSRF-Token`)
- SSH key management per user
- OIDC / OAuth2 optional integration
- Scoped access tokens with optional expiration
- Repository deploy keys (read-only or read-write)
- ENV-driven config with fail-fast on missing secrets

### Added — Git Hosting
- Smart HTTP transport (clone, push, pull over HTTP)
- AGit protocol (`refs/for/` push for instant PR creation)
- Branch management, commit log, diff viewing
- Git LFS per-repository (configurable file size limits)
- Branch protection rules (force-push blocking)
- Repository visibility (public / private)

### Added — Collaboration
- Pull requests (open / merged / closed) with author tracking
- Issues (open / closed)
- Reviewer assignment (default reviewer per repo, per-PR overrides)
- Merge strategy selection per repository (merge / squash / rebase)
- Branching model configuration (feature / bugfix / release / hotfix prefixes)
- PR default description templates + excluded-files configuration
- Webhook system with event filtering (push, pull_request, issue)
- Repository member RBAC (read / write / admin)

### Added — Frontend SPA
- React 18 + TypeScript + Vite, embedded into Go binary via `//go:embed`
- 20 route-level pages covering auth, dashboard, repos, code, PRs, issues, and settings
- Triple-state sidebar: expanded (320px) / collapsed (56px) / mobile bottom bar
- Mobile-first responsive design (375px → 1440px)
- DiffViewer (side-by-side + unified), MobileComment (bottom-sheet), TreeBrowser

### Added — Design System
- Custom semantic token palette in `frontend/src/ui/tokens.ts`
- Full dark/light mode via Tailwind CSS v4 `@variant dark`
- 8px grid system; 44px minimum touch targets (WCAG 2.5.5)
- System font stack (Segoe UI, Roboto, sans-serif)

### Added — Infrastructure
- PostgreSQL + XORM with migrations 001–007
- ActivityPub actor data model (FederationActor) — data layer only
- Docker Compose for local PostgreSQL + NATS
- Makefile: dev, build, migrate, test, lint, docker-up

---

[Unreleased]: https://github.com/forgeo/forgebucket/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/forgeo/forgebucket/compare/v0.9.0...v1.0.0
[0.9.0]: https://github.com/forgeo/forgebucket/compare/v0.8.0...v0.9.0
[0.8.0]: https://github.com/forgeo/forgebucket/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/forgeo/forgebucket/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/forgeo/forgebucket/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/forgeo/forgebucket/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/forgeo/forgebucket/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/forgeo/forgebucket/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/forgeo/forgebucket/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/forgeo/forgebucket/releases/tag/v0.1.0
