Architecture decisions¶
Architecture-level choices are recorded as ADRs in
docs/decisions/
in the repository. This page indexes them; the linked files are authoritative.
Feature-level decisions live in the relevant OpenSpec change's design.md
rather than here.
Index¶
ADR 0001 — Use Java for the product¶
Accepted, 2026-08-13.
Java, compiled with GraalVM native-image. The finalists were Java and Rust, and the deciding factor was git capability at the product horizon: embedded upload-pack serving, object-storage-native repositories, and fetch-time composition of virtual marketplaces exist today, production-proven, in JGit and Gerrit. Choosing Rust would have bet the roadmap on gitoxide's server side maturing on schedule.
Native-image also shrinks Java's classic dynamic attack surface through closed-world compilation, and gives single-artifact installs.
ADR 0002 — Toolchain and product decisions¶
Accepted, 2026-08-13.
The concrete stack that follows from 0001:
| Area | Decision |
|---|---|
| Runtime | Java 25, Spring Boot 4 / Spring Framework 7 |
| Build | Maven + wrapper; Maveniverse Nisse derives the version from git state |
| Quality | Spotless (palantir-java-format) owns formatting; Checkstyle carries rules only |
| Git | JGit from day one — no subprocess-git phase, ever |
| Data | JdbcClient + Flyway on PostgreSQL; JPA/Hibernate rejected as weight a small schema does not need |
| Dev/test | Arconia Dev Services — zero-config Testcontainers for PostgreSQL |
| Release | GraalVM native-image as the release profile |
| Auth | OIDC-only SSO for humans; the app is its own BFF; PATs for git clients |
The JGit decision is the one with teeth: Java was chosen for JGit, so carrying a temporary subprocess architecture would have paid Java's costs without its payoff.
ADR 0003 — Agentic-first frontend stack¶
Accepted, 2026-08-13.
The portal is developed agent-first, so the design constraint is a closed SDLC loop: an agent must be able to build, run, inspect, interact, screenshot, test, diagnose and modify without a human acting as eyes and hands.
| Layer | Choice |
|---|---|
| Language / framework | TypeScript (strict), React |
| Build | Vite — no Next.js; the app-as-BFF serves static assets, so SSR is dead weight |
| Styling / components | Tailwind CSS, shadcn/ui copy-in source on exactly one headless primitive layer |
| Server state | TanStack Query against same-origin /api with a session cookie |
| Forms | React Hook Form + Zod |
| API mocking | MSW, contract-derived only — generated from the backend's OpenAPI, never hand-authored, and never in the acceptance path |
The verification harness is described in the ADR as "the load-bearing half": Playwright is the agent's browser and the outer loop, asserting against accessibility-tree snapshots. A hand-written mock is a second API implementation that drifts silently, which is why the acceptance path forbids them.
ADR 0004 — skillsgateway organization and coordinates¶
Accepted, 2026-08-17.
One name everywhere, settled before the first published artifact: GitHub org and
repo skillsgateway/skillsgateway, Maven groupId dev.skillsgateway
(backed by the skillsgateway.dev domain), prefixed artifactIds per deliverable
(skills-gateway-server for the gateway), and package-per-artifact Java
roots (dev.skillsgateway.server). The container image is
ghcr.io/skillsgateway/skillsgateway; the Helm chart name and reqstool URN
stay skills-gateway. Amends the coordinates item of ADR 0002.
ADR 0005 — Signed provenance stays in Phase 3, with named pull-forward triggers¶
Proposed, 2026-08-18.
Signed in-toto/Sigstore attestations remain a Phase-3 item. The recorded provenance chain (content-addressed snapshots + vetting verdicts + approval records + append-only ledger) already binds upstream SHA → scan → approval → published artifact inside one trust domain; signing pays off only when verification happens outside it. Four pull-forward triggers are named — OCI re-publication/federation, an operator-independent audit demand, cross-gateway promotion, and consumer-side verification tooling — any one of which reopens the decision.
ADR 0006 — Embedded CEL for policy rules, not an OPA sidecar¶
Accepted, 2026-08-18.
Policy deny rules are embedded cel-java expressions — compiled to a boolean at write time, evaluated fail-closed inside the approval gate — rather than an OPA/Rego sidecar: no second deployment unit, no network hop inside the gate, and CEL's non-Turing-complete, terminating semantics are the right blast radius for user-authored rules and a playground pointed at real snapshots. Scope is deny-only on purpose; auto-approval would delegate the human gate and stays parked as a product decision.
ADR 0007 — First-party hosting: a write path, deliberately somewhere else¶
Accepted, 2026-08-23.
The gateway accepts git pushes for marketplaces it hosts itself — on a
separate endpoint (/publish/**, so the serving facade keeps its null
receive-pack factory), into a separate repository (the publisher's origin,
so quarantine keeps exactly one writer), under a separate token scope that
no existing token holds and that has no every-marketplace form. One lineage,
forward only unless the marketplace says otherwise, and the approval gate is
untouched: pushed content is quarantined, vetted and held like anything
fetched. Auto-approval for trusted internal publishers stays parked per ADR
0006.
ADR 0008 — The serving surface stays the embedded facade; SSO closes at the gateway¶
Accepted, 2026-08-23.
Serving approved content out of an external forge was weighed and declined: it would outsource authentication and visibility, which are commodities, at the price of outsourcing enforcement and audit, which are the product — a forge-served repository makes the approval gate bypassable by anyone with repository write, and forge logs do not record per-user fetches. The two things a forge was wanted for are addressed at the gateway instead: SSO-derived short-lived git credentials (GW_0104, implemented) close the second-credential-system gap without moving a fetch off the ledger, and a read-only forge mirror for browsing is sequenced after, never as a serving surface. Availability of the facade is now explicitly a security property, with serving independent of ingestion.
Related¶
- Architecture — the full architectural narrative: threat model, connector-based vetting, two-repo promotion, observability and the phase roadmap.
docs/language-decision.md— the Java-versus-Rust comparison behind ADR 0001.docs/reqstool/— requirements and verification cases, the single source of truth for requirement text.
Adding one¶
New architecture-level choices get a new numbered ADR in docs/decisions/,
referenced from the architecture document, and an entry
on this page.