Files
ForgeBucket/CHANGELOG.md
T

25 KiB
Raw Permalink Blame History

Changelog

All notable changes to ForgeBucket are documented here.

Format follows Keep a Changelog. Versions follow Semantic Versioning.


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 endpointsGET /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 endpointsGET /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 endpointsGET /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 endpointsGET /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_sbomSBOMReport (repoId, runId, sha, format, componentCount, bomDocument, generatedAt)
  • Migration 017_ociOCIRepository, OCIManifest, OCITag, OCIBlob, OCIUpload
  • Migration 018_scanningSecretLeak (repoId, commitSha, ref, patternName, description, severity, matchSample, dismissed, dismissedBy, dismissedAt, detectedAt)
  • Migration 019_vulnscanVulnerabilityFinding (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.goCheck(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.goCheckDrift 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.goTriggerSync 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 300s); 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 001007
  • ActivityPub actor data model (FederationActor) — data layer only
  • Docker Compose for local PostgreSQL + NATS
  • Makefile: dev, build, migrate, test, lint, docker-up