Files
ForgeBucket/AGENTS.md
T
erangel1 edf3c9824e Phase 3C — Commit Summary
feat: workspaces — collaborative repo namespaces
Backend
- internal/models/workspace.go — Workspace (handle, displayName,
  description, createdBy) + WorkspaceMember (workspaceId, userId,
  username, role: owner/admin/member)
- internal/models/repo.go — added nullable workspace_id column; existing
  user repos unaffected
- internal/models/migrations/011_workspaces.go — syncs both tables +
  adds column to repository
- internal/api/handlers/workspace.go — ListWorkspaces, CreateWorkspace,
  GetWorkspace, UpdateWorkspace, DeleteWorkspace (blocks if repos
  remain), ListRepos, ListMembers, AddMember, UpdateMember, RemoveMember
- internal/api/handlers/repos.go — lookupRepo resolves workspace
  handles; Create accepts workspace field; List includes workspace
  member repos; withOwnerName uses workspace handle for workspace-owned
  repos
- internal/api/handlers/dashboard.go — recentRuns + repo list include
  workspace repos the user is a member of
- internal/api/router.go — /workspaces, /workspaces/:handle/* routes
  Workspace rules enforced:
- Handle globally unique across usernames + workspace handles (409 on
  collision)
- Creator auto-assigned owner role
- Delete blocked if repos exist
- Last owner cannot be demoted/removed
  ---
  feat: secret management hierarchy (Global → Workspace → Repo → Env)
  Backend
- internal/models/secret.go — Secret struct +
  EncryptSecret/DecryptSecret with AES-256-GCM (key = SHA-256 of
  SESSION_SECRET); values never serialised to JSON
- internal/models/migrations/012_secrets.go — syncs secret table
- internal/api/handlers/secret.go — List/Upsert/Delete for all four
  scopes; ResolveSecretsForRun builds merged env map for CI
- internal/domain/ci/executor.go — JobContext.Secrets field; secrets
  injected as --env KEY=VALUE into docker run; buildJobContext calls
  resolveSecrets(Global < Workspace < Repo < Env)
- internal/domain/ci/runner_manager.go — passes cfg.SessionSecret to
  buildJobContext
- internal/api/router.go — /repos/:owner/:repo/secrets,
  /environments/:envName/secrets, /workspaces/:handle/secrets,
  /admin/secrets
  ---
  feat: workspace + secret management UI
  Frontend
- types/api.ts — Workspace, WorkspaceWithMeta, WorkspaceMember,
  SecretListItem types
- api/queries/workspaces.ts — full CRUD hooks + WorkspaceRepo type
- api/queries/secrets.ts — repo/env/workspace secret hooks
- pages/WorkspacesPage.tsx — list + create modal
- pages/WorkspacePage.tsx — workspace dashboard with repo list
- pages/WorkspaceSettingsPage.tsx — general settings, members CRUD,
  workspace secrets, danger zone
- pages/RepoSecretsPage.tsx — repo secrets + per-environment secret
  sections with priority hierarchy callout
- pages/CreateRepoPage.tsx — ?workspace= query param pre-fills owner
  selector; only admin/owner workspaces shown
- components/layout/Sidebar.tsx — "Workspaces" global nav item +
  workspace quick-links; "Secrets" in RepoSubNav; new SecretsIcon,
  WorkspaceIcon
- App.tsx — routes for /workspaces, /workspaces/:handle,
  /workspaces/:handle/settings, /repos/:owner/:repo/secrets
2026-05-11 23:34:46 +02:00

9.6 KiB

AGENTS.md — ForgeBucket AI Agent Guide

This file is for AI coding agents (Claude Code, GitHub Copilot, etc.) working in this repository. Read it before making changes.


Mission

ForgeBucket is a unified operating system for software delivery — not a Git repository viewer with CI attached. The guiding philosophy:

Repositories are runtime systems. The dashboard is a command center. GitOps is first-class.

Every feature decision should answer: does this reduce cognitive load? Does this improve operational awareness? Does this improve developer flow?

The full product vision lives in ai_agent_master_prompt_for_building_modern_git_platform.md.


Architecture Map

cmd/forgebucket/          — binary entry point (main.go)
internal/
  api/
    router.go             — Chi router, all route definitions (~26 routes)
    middleware/           — auth, CSRF, RBAC, logging
    handlers/             — one file per domain (repo, pr, issue, auth, user, ssh...)
  domain/
    git/                  — sanitized git binary wrapper (exec.Command only, no shell)
    federation/           — ActivityPub / ForgeFed (DATA LAYER ONLY — no handlers yet)
    ci/                   — CI orchestrator (EMPTY — Phase 2 stub)
  models/                 — XORM structs + 7 migration files
  config/                 — ENV-driven config, fails fast on missing secrets
web/                      — //go:embed target for the built React SPA
frontend/
  src/
    pages/                — 20 route-level page components
    components/           — shared UI (AppShell, Sidebar, Header, DiffViewer, etc.)
    ui/
      tokens.ts           — SINGLE SOURCE OF TRUTH for all design tokens
    hooks/                — custom React hooks
    api/                  — typed API client (fetch wrappers)

Middleware chain — this order is fixed, do not reorder:

Logger → RealIP → Recoverer → CORS → CSRF → SessionAuth → RBAC → Handler

Current Phase Status

Understand the phases before adding code — don't build Phase 3 infrastructure when Phase 2 is incomplete.

Phase Scope Status
1 Auth, Git HTTP, repos, PRs, issues, RBAC, webhooks, LFS, design system, 20-page SPA Complete
2A NATS event bus, WebSocket hub upgrade, audit log Complete
2B CI orchestrator, runner manager, Docker executor, artifact registry Complete
2C Pipeline DAG visualization, dashboard CI upgrade, command palette wiring Complete
3A Environment model + deployment tracking Complete
3B Unified operational timeline Complete
3C Workspaces + secret management hierarchy Active
3D GitOps controller + drift detection Planned
3E Observability (Prometheus, health sparklines) Planned
3F Federation handlers (ActivityPub inbox/outbox) Planned
4 AI diagnostics, signed artifacts, OCI registry, secret/dep scanning Planned

Do not implement Phase 3+ features without explicit discussion. The domain/federation/ directory is an intentional stub — the data model exists but no HTTP handlers should be wired until Phase 3F.

Phase 3A — What to Build

Backend and frontend are both net-new for Phase 3A. Nothing exists yet.

Backend:

  1. internal/models/environment.goEnvironment (id, repoId, name, url, protectionRules JSON) + Deployment (id, envId, repoId, sha, ref, status, triggeredBy, description, runId, startedAt, finishedAt)
  2. internal/models/migrations/010_environments.goRun010() syncing both structs; call from 001_init.go
  3. internal/api/handlers/environment.goListEnvironments, CreateEnvironment, GetEnvironment, UpdateEnvironment, DeleteEnvironment, ListDeployments, CreateDeployment, UpdateDeploymentStatus; publish deployment.* NATS events
  4. internal/api/router.go — wire routes under /{owner}/{repo}/environments and /{owner}/{repo}/environments/{envName}/deployments

Frontend: 5. frontend/src/types/api.ts — add Environment, Deployment, DeployStatus types 6. frontend/src/api/queries/environments.tsuseEnvironments, useEnvironment, useCreateEnvironment, useUpdateEnvironment, useDeleteEnvironment, useDeployments, useCreateDeployment, useUpdateDeploymentStatus 7. frontend/src/pages/EnvironmentsPage.tsx — environment cards each showing latest deployment status, SHA, who deployed, time; "New environment" flow; deployment history per env 8. frontend/src/components/layout/Sidebar.tsx — add Environments nav item between Pipelines and Settings in RepoSubNav 9. frontend/src/pages/RepoPage.tsx — surface deployment status badges in the repo header (latest deploy per env at a glance) 10. frontend/src/App.tsx — add route repos/:owner/:repo/environments


Go Conventions

Git commands — critical rule

Always use exec.Command("git", arg1, arg2, ...) with discrete arguments. Never build a shell string from user input.

// CORRECT
cmd := exec.Command("git", "clone", "--bare", repoURL, destPath)

// WRONG — never do this
cmd := exec.Command("sh", "-c", "git clone "+userInput)

This rule is non-negotiable. It prevents command injection.

Router / handlers

  • Chi router. Route definitions in internal/api/router.go.
  • One handler file per domain area. Keep handlers thin — business logic belongs in domain packages.
  • All POST/PUT/DELETE routes require X-CSRF-Token header matching the session cookie. The middleware enforces this, but don't remove it from routes.

Database

  • XORM for all DB access. Structs in internal/models/.
  • Migrations are numbered files in internal/models/migrations/. Always add a new migration file; never edit existing ones.
  • No raw SQL strings built from user input.

Secrets and config

  • All secrets come from environment variables via internal/config/.
  • Never hardcode secrets, tokens, or credentials anywhere.
  • SESSION_SECRET ≥ 32 chars. CSRF_SECRET = exactly 32 chars.

Error handling

  • Return errors up the call stack. Don't swallow them silently.
  • HTTP handlers use consistent JSON error responses — follow the pattern in existing handlers.

TypeScript / React Conventions

Design tokens — critical rule

All spacing, color, and sizing values must come from frontend/src/ui/tokens.ts. Do not introduce new hardcoded pixel values or color hex codes.

// CORRECT — use token classes via Tailwind
<div className="p-sm gap-xs">

// WRONG — arbitrary values
<div style={{ padding: "12px" }}>

Grid system

  • Base unit: 8px. All spacing is multiples of 4px (xs) or 8px (sm).
  • Touch targets: 44px minimum height/width on all interactive elements (buttons, links, icon buttons).

Dark mode

  • Use Tailwind v4 @variant dark — not hardcoded dark: classes unless inside a component that explicitly handles both.
  • Colors must work in both light and dark modes. Test both.

Component patterns

  • AppShell wraps all authenticated pages. Don't bypass it.
  • Sidebar has three states: expanded (320px), collapsed (56px), mobile bottom bar. Respect all three.
  • Mobile-first: design for 375px, enhance for 1440px. Use the BottomTabBar for mobile nav — don't add new nav patterns.
  • Loading states: use Skeleton components for perceived performance, not spinners.
  • Mobile code review: use the MobileComment bottom-sheet pattern — not modals.

API calls

  • Use the typed API client in frontend/src/api/ — don't write raw fetch calls in components.
  • Always include X-CSRF-Token header on mutating requests.

What NOT to Do

  • No shell string injection — see Go conventions above
  • No hardcoded secrets — everything via env
  • No skipping CSRF — all mutating routes require it
  • No arbitrary design values — tokens.ts is the law
  • No Phase 3+ features without discussion — don't wire up GitOps, federation handlers, or the command palette until Phase 2 is complete
  • No new color tokens — if the design requires a new color, discuss it; don't invent one
  • No modal-heavy UX — this platform uses progressive disclosure; avoid deep modal chains
  • No YAML-centric UI — pipeline and environment config should feel operational, not config-file editing

Testing

make test     # Go tests (go test ./...) + Vitest (frontend unit tests)
make lint     # go vet + ESLint
  • Go: test files live alongside source files (*_test.go)
  • Frontend: Vitest for unit tests, component tests alongside components
  • All UI changes must be manually verified at 1440px desktop and 375px mobile

Key Files Reference

File Purpose
internal/api/router.go All route definitions — start here for backend
internal/models/ XORM models + migrations — all DB schemas
internal/config/config.go Env-driven config, required vars
internal/domain/git/ Git binary wrapper — safe exec patterns
frontend/src/ui/tokens.ts Design token source of truth
frontend/src/components/AppShell.tsx Root layout wrapper
frontend/src/components/Sidebar.tsx 3-state navigation sidebar
frontend/src/pages/ All 20 route-level pages
frontend/src/api/ Typed API client
.env.example All required environment variables
CLAUDE.md Developer guide (rules overlap with this file — CLAUDE.md takes precedence on conflicts)

Running Locally

cp .env.example .env     # fill SESSION_SECRET and CSRF_SECRET
make docker-up           # PostgreSQL via Docker Compose
make migrate             # run XORM migrations
make dev                 # Go :8080 + Vite :5173