Files
apix-mvp/WORKLOG.md
T
Carsten Rehfeld b2a16a8be7 Implement apix-registry with IoT sunset/decommission lifecycle and full BDD suite
- REST API: register, patch, O-level, replacements, history, search endpoints
- IoT lifecycle validations: future sunset, lock-before-release, sunset-passed-before-decommission
- DB schema: Liquibase changesets 001–008 (services, versions, replacements, sunset-at column)
- @ColumnTransformer(write="?::jsonb") on bsm_payload fields to avoid JDBC varchar→jsonb rejection
- Jandex plugin on apix-common + quarkus.index-dependency so @NotBlank validators resolve at runtime
- quarkus-logging-json extension added; quarkus.log.console.json=false is now a recognised key
- Fix requireSunsetBeforeLockRelease: Boolean.TRUE.equals instead of !Boolean.FALSE.equals (null guard)
- BDD suite: 27 scenarios / 213 steps across 5 feature files (sunset-lock, decommission, replacement, discovery, anonymity)
- Test infrastructure: JDBC TRUNCATE in @Before for DB isolation, Arc.container() for clock control — no test endpoints in production code
- sunsetAt truncated to microseconds in BDD steps to match Postgres timestamptz precision
- Cucumber step fixes: singular/plural candidate(s), lastResponse propagation in replacementsReturnsNCandidates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 09:13:26 +02:00

38 KiB
Raw Blame History

APIX MVP — Work Log

Goal: Deployable, publicly queryable APIX registry PoC on Hetzner by end of 2026. Constraint: Solo development, LLM-assisted. Security hygiene (not hardening). Speed over completeness. Success criterion: A reviewer can open a public URL, run a capability query, see a result from a registered service, and confirm the Spider has checked it.


Status Legend

Symbol Meaning
[ ] Not started
[~] In progress
[x] Done
[!] Blocked / decision needed

Arc42 Documentation Deliverables

1. Introduction and Goals → docs/arc42/01-introduction-goals.md

# Deliverable Status
D-01 MVP goal statement (what must be provable at the end) [ ]
D-02 Quality goals table (top 35, measurable) [ ]
D-03 Stakeholder table (STF reviewer, agent developer, service registrant, BSF) [ ]
D-04 Explicit out-of-scope list for MVP (billing, full trust model, multi-region) [ ]

2. Architecture Constraints → docs/arc42/02-constraints.md

# Deliverable Status
D-05 Technical constraints (Hetzner, Docker Compose, Python, PostgreSQL, open source stack) [ ]
D-06 Organisational constraints (solo dev, LLM-assisted, public GitHub repo required) [ ]
D-07 Regulatory constraints (HTTPS mandatory, GDPR-lite: no PII stored beyond registrant email) [ ]
D-08 Convention constraints (HATEOAS API style, IETF Internet-Draft alignment) [ ]

3. Context and Scope → docs/arc42/03-context-scope.md

# Deliverable Status
D-09 System context diagram — PlantUML, external actors: Agent, Service Registrant, Spider, Admin [ ]
D-10 External interface table (what each actor sends/receives) [ ]
D-11 Technical context diagram — PlantUML, network boundary (Caddy → API → DB, Spider → external services) [ ]

4. Solution Strategy → docs/arc42/04-solution-strategy.md

# Deliverable Status
D-12 Tech stack decision table with rationale (FastAPI, PostgreSQL JSONB, Caddy, HTMX) [ ]
D-13 Architectural pattern decisions (REST + HATEOAS, async Spider, single-process portal) [ ]
D-14 Quality goal → architecture decision mapping [ ]
D-15 MVP shortcuts explicitly listed (manual O-level assignment, no billing, no rate limiting beyond Caddy) [ ]

5. Building Block View → docs/arc42/05-building-blocks.md

# Deliverable Status
D-16 Level 1 diagram — PlantUML: API service, Spider service, Portal, PostgreSQL, Caddy [ ]
D-17 Level 2 diagram — API internals: router, service layer, BSM validator, DB adapter [ ]
D-18 Level 2 diagram — Spider internals: scheduler, fetcher, liveness evaluator, DB writer [ ]
D-19 Component responsibility table (one sentence per component) [ ]

6. Runtime View → docs/arc42/06-runtime-view.md

# Deliverable Status
D-20 Scenario 1: Agent queries registry by capability — PlantUML sequence diagram [ ]
D-21 Scenario 2: Service registrant submits BSM via portal — PlantUML sequence diagram [ ]
D-22 Scenario 3: Spider runs liveness check and updates service status — PlantUML sequence diagram [ ]
D-23 Scenario 4: Agent navigates index via HATEOAS links — PlantUML sequence diagram [ ]

7. Deployment View → docs/arc42/07-deployment-view.md

# Deliverable Status
D-24 Hetzner deployment diagram — PlantUML: VPS, Docker Compose services, Caddy, volumes [ ]
D-25 Environment table (dev / staging / prod differences) [ ]
D-26 Backup and restore strategy (pg_dump, Hetzner volume snapshot) [ ]
D-27 Domain and DNS setup notes [ ]

8. Crosscutting Concepts → docs/arc42/08-crosscutting-concepts.md

# Deliverable Status
D-28 Logging concept (structured JSON, levels, what is logged per component) [ ]
D-29 Error handling concept (HTTP error codes, error response schema) [ ]
D-30 Security hygiene concept (HTTPS via Caddy, API key on write endpoints, rate limiting, no PII logging) [ ]
D-31 BSM validation concept (schema version, required fields, validation error format) [ ]
D-32 Liveness check concept (what "live" means, check frequency, status transition rules) [ ]
D-33 Idempotency concept (re-registration behaviour, Spider re-check behaviour) [ ]
D-33a i18n concept (locale resolution order, @MessageBundle keys, help content localisation, language switcher) [ ]

9. Architecture Decisions → docs/arc42/09-architecture-decisions.md

# ADR Status
D-34 ADR-001: Python + FastAPI over Node/Go — rationale: AI ecosystem, speed, solo dev [ ]
D-35 ADR-002: PostgreSQL + JSONB over MongoDB — rationale: relational integrity for registry + flexible BSM payload [ ]
D-36 ADR-003: Caddy over nginx/traefik — rationale: auto-TLS, zero config, solo maintainability [ ]
D-37 ADR-004: HTMX + Jinja2 over React/Vue — rationale: no JS build pipeline, speed, portal is admin-grade not consumer-grade [ ]
D-38 ADR-005: Automated O-1/O-2/O-3 verification in MVP — DNS + GLEIF + OpenCorporates + HTTP hygiene checks; O-4/O-5 post-MVP [ ]
D-39 ADR-006: Two-VPS Hetzner deployment — apix-app (Swarm) + apix-gitea (Gitea + CI) [ ]
D-47 ADR-007: Register verification APIs (GLEIF, OpenCorporates, EU Sanctions) as reference APIX entries [ ]
D-48 ADR-009: Maven multi-module with separated Spider module [ ]
D-49 ADR-010: Self-hosted Gitea primary; GitHub push mirror [ ]
D-50 ADR-011: Docker Swarm single-node for zero-downtime production deployment [ ]
D-51 ADR-012: Three-stage CI/CD pipeline (fast / native build / deploy) [ ]
D-52 ADR-013: Server-side i18n via Quarkus @MessageBundle; EN + DE; cookie + Accept-Language locale resolution [ ]
D-53 ADR-014: Client-side help overlay engine; server-rendered locale-aware tour content; 5 tours (register, search, trust, admin, agent-setup) [ ]

10. Quality Requirements → docs/arc42/10-quality-requirements.md

# Deliverable Status
D-40 Quality tree (functionality, reliability, security hygiene, operability) [ ]
D-41 Quality scenarios table (stimulus → response → measurable outcome) [ ]
D-42 MVP acceptance criteria (what "done" looks like — the STF reviewer test) [ ]

11. Risks and Technical Debt → docs/arc42/11-risks-technical-debt.md

# Deliverable Status
D-43 Risk register (chicken-and-egg, big tech competition, single point of failure, solo bus factor) [ ]
D-44 Technical debt log (MVP shortcuts accepted, with explicit exit path for each) [ ]
D-45 Mitigation actions per risk [ ]

12. Glossary → docs/arc42/12-glossary.md

# Deliverable Status
D-46 Glossary: APIX, BSM, Spider, O-level, S-level, Liveness, AE, HATEOAS, DC-1 [ ]

Code Deliverables

Stack: Java 21 + Quarkus 3.x, Maven multi-module. Five modules: two plain Java libraries (apix-common, apix-verification) and three independently deployable Quarkus apps (apix-registry, apix-spider, apix-portal). Spider runs as its own process — separate module, separate container, separate lifecycle. Build tool: Maven with parent POM as BOM. Tests: plain JUnit 5 for library modules; JUnit 5 + @QuarkusTest + RestAssured + WireMock for Quarkus modules.

Source root per module: <module>/src/main/java/org/botstandards/apix/<module-suffix>/


Parent POM — pom.xml

# Deliverable Status
C-00 pom.xml — parent; imports Quarkus BOM; declares all five modules; manages maven-compiler-plugin (Java 21 release); quarkus-maven-plugin config inherited by Quarkus modules [ ]

apix-common — Shared Library (plain Java 21)

No Quarkus dependency. Shared enums and DTOs used by all modules.

# Deliverable Status
C-01 OLevel.java — enum: UNVERIFIED, IDENTITY_VERIFIED, LEGAL_ENTITY_VERIFIED, HYGIENE_VERIFIED, OPERATIONALLY_VERIFIED, AUDITED [ ]
C-02 LivenessStatus.java — enum: PENDING, LIVE, DEGRADED, UNREACHABLE [ ]
C-03 BsmPayload.java — Java record; all BSM fields per Internet-Draft; Bean Validation annotations (@NotBlank, @Valid, @URL) [ ]
C-04 ServiceSummaryDto.java — Java record; fields exposed in search results (id, name, capabilities, olevel, liveness_status, endpoint, last_checked_at) [ ]
C-05 VerificationResult.java — Java record: olevel achieved, step that blocked (if any), message [ ]

apix-verification — Verification Library (plain Java 21)

No Quarkus dependency. Uses java.net.http.HttpClient and dnsjava. All classes are plain CDI-free POJOs — fully testable with plain JUnit. Depends on apix-common.

# Deliverable What it does O-level unlocked
C-06 O1DnsVerifier.java DNS TXT record lookup (dnsjava); business email MX check O-1
C-07 O2GleifVerifier.java GLEIF REST API via java.net.http.HttpClient; LEI lookup by company name + jurisdiction O-2
C-08 O2OpenCorporatesVerifier.java OpenCorporates REST API fallback for registrants without LEI; covers 130+ jurisdictions O-2 (fallback)
C-09 O3HygieneVerifier.java HTTP fetch of /.well-known/security.txt; DNS DMARC + SPF check; policy URL reachability O-3
C-10 SanctionsScreener.java Screens name + jurisdiction against locally cached OFAC/EU/UN lists (CSV/XML read from file path) Pre-condition for O-2
C-11 VerificationPipeline.java Orchestrates O-1 → sanctions → O-2 → O-3 in sequence; returns VerificationResult; no I/O side effects (caller persists result) All
C-12 VerificationConfig.java Plain POJO: gleifApiUrl, openCorporatesApiKey, sanctionsCachePath — injected by caller (Quarkus @ConfigProperty in registry)

apix-registry — REST API (Quarkus 3.x app)

Depends on apix-common + apix-verification. Owns the database schema (runs Liquibase at startup). Quarkus extensions: RESTEasy Reactive, Hibernate ORM + Panache, PostgreSQL JDBC, Liquibase, SmallRye Health, Quarkus Security.

Resource layer

# Deliverable Status
C-13 resource/IndexResource.javaGET / HATEOAS root; navigation links JSON [ ]
C-14 resource/ServiceResource.javaGET /services (capability search + filters), GET /services/{id} [ ]
C-15 resource/RegisterResource.javaPOST /register (API-key protected); triggers VerificationOrchestrator asynchronously [ ]

Service layer

# Deliverable Status
C-16 service/RegistryService.java — register (UPSERT on endpoint URL), search by capability (JPQL + JSONB), dedup [ ]
C-17 service/VerificationOrchestrator.java — CDI bean; injects VerificationConfig from Quarkus @ConfigProperty; calls VerificationPipeline; persists VerificationResult to ServiceRecord; fires admin notification event if manual step needed [ ]

Model + repository

# Deliverable Status
C-18 model/ServiceRecord.java — Panache entity; services table; JSONB via @JdbcTypeCode(SqlTypes.JSON) for bsm_payload [ ]
C-19 repository/ServiceRepository.java — capability search query; upsert; O-level update [ ]

Liquibase migrations

# Deliverable Status
C-20 resources/db/changelog/db.changelog-master.xml [ ]
C-21 changes/001-initial-schema.xmlservices table: id, endpoint_url (unique), bsm_payload (JSONB), olevel, slevel, liveness_status, registered_at [ ]
C-22 changes/002-verification-columns.xml — verification_status, olevel_checked_at, sanctions_cleared, gleif_lei [ ]
C-23 changes/003-liveness-metrics.xml — last_checked_at, uptime_30d_percent, avg_response_ms, consecutive_failures [ ]

Configuration

# Deliverable Status
C-24 resources/application.properties — datasource, Liquibase enabled, port 8180, GLEIF URL, OpenCorporates key, sanctions cache path, log level [ ]

apix-spider — Liveness Scheduler (Quarkus 3.x app)

Separate module, separate container, separate lifecycle. Depends on apix-common. Connects to same PostgreSQL DB; Liquibase disabled (quarkus.liquibase.migrate-at-start=false). Quarkus extensions: Quarkus Scheduler, Hibernate ORM + Panache, PostgreSQL JDBC, REST Client Reactive, SmallRye Health.

# Deliverable Status
C-25 SpiderScheduler.java@Scheduled(every="${spider.interval:15m}"); loads all active services; dispatches to LivenessFetcher via virtual thread pool [ ]
C-26 LivenessFetcher.java@RestClient; async HTTP GET with 5s timeout; @RunOnVirtualThread [ ]
C-27 LivenessEvaluator.java — pure logic: HTTP status code + response time ms → LivenessStatus; no I/O; no Quarkus dependency [ ]
C-28 OpenApiParser.java — fetch + parse OpenAPI spec; verify declared capabilities present [ ]
C-29 McpParser.java — fetch + parse MCP spec URL [ ]
C-30 model/SpiderServiceView.java — Panache entity (read/write subset of services table): endpoint_url, liveness_status, last_checked_at, uptime_30d_percent, avg_response_ms, consecutive_failures [ ]
C-31 repository/SpiderRepository.java — load services due for check; write liveness result [ ]
C-32 resources/application.properties — datasource, Liquibase disabled, scheduler interval, spider HTTP timeout, port 8082 (internal only) [ ]

apix-portal — Web Portal (Quarkus 3.x app)

Depends on apix-common. Calls apix-registry via REST Client — no direct DB access. Quarkus extensions: RESTEasy Reactive, Qute, REST Client Reactive, SmallRye Health.

# Deliverable Status
C-33 resource/PortalResource.javaGET /, /services/{id}, /search, /register; delegates to RegistryClient [ ]
C-34 resource/AdminResource.javaGET /admin, POST /admin/olevel (API-key protected) [ ]
C-35 client/RegistryClient.java@RegisterRestClient; two methods: search(capability, page)List<ServiceSummaryDto>; getDetail(id)ServiceDetailDto (full BSM payload + all registry metadata); typed REST client calling apix-registry [ ]
C-36 templates/index.html (Qute, @CheckedTemplate) — registry stats, search box [ ]
C-37 templates/register.html (Qute) — BSM registration form; HTMX inline validation [ ]
C-38 templates/service.html (Qute) — human-targeted service detail page; see layout spec below [ ]
C-39 templates/search.html (Qute) — capability search results, HTMX pagination [ ]
C-40 templates/admin.html (Qute) — pending verifications, O-level assignment, reference registration flags [ ]
C-41 resources/META-INF/resources/style.css — minimal CSS, no framework [ ]
C-42 resources/application.properties — registry base URL, port 8081, log level [ ]

service.html — human-readable service detail page (C-38 layout spec)

A human visiting /services/{id} is evaluating whether to use this service. The page answers four questions in order: Who is this? Can I trust them? What exactly does it do? How do I call it?

Section 1 — Identity hero

  • Service name (h1, prominent)
  • description (full text, not truncated)
  • Two inline trust chips: O-level badge (colored pill: grey=O-0, blue=O-1/O-2/O-3, green=O-4/O-5) + liveness dot (green/amber/red) — both visible above the fold without scrolling

Section 2 — Trust verification card

  • O-level: large badge icon + level name (e.g. "Legal Entity Verified") + 2-sentence plain-English explanation (e.g. "The provider's legal incorporation has been confirmed against the GLEIF global legal entity database. This means APIX has independently verified that the registrant is a real legal entity — not self-declared.") + GLEIF LEI link if present
  • S-level: same pattern (badge + name + explanation)
  • "Verified on" date — relative ("3 days ago") with absolute date as tooltip; "Reference entry by BSF" badge if applicable (grey label "Registered by Bot Standards Foundation — operator not yet self-registered")

Section 3 — Liveness status card

  • Colored status row: dot + label (LIVE / DEGRADED / UNREACHABLE / PENDING)
  • Uptime last 30 days: percentage bar + number (e.g. "98.4%")
  • Average response time: "142 ms"
  • Last checked: relative time ("8 minutes ago")
  • Check frequency note: "Automatically verified every 15 minutes by the APIX Spider"

Section 4 — Capabilities

  • Each capabilities[] entry as a visual chip (tag-style)
  • If BSM includes a description per capability, show it beneath the chip

Section 5 — Pricing (conditional; omit section if BSM has no pricing object)

  • Per-call price + currency + billing unit (e.g. "€0.02 / call")
  • Billing model if declared
  • "Pricing not declared — contact provider" if the BSM pricing object is absent

Section 6 — Contact & registration

  • Registrant contact email (mailto link)
  • Registered since (human date)
  • BSM version

Section 7 — Integration (<details> collapsible; closed by default)

  • Endpoint URL with one-click copy button
  • OpenAPI spec URL link + MCP spec URL link (if declared in BSM)
  • Minimal HTTP example snippet:
    GET {endpoint}
    X-APIX-Caller: your-agent-id
    
  • Link to machine-readable entry: GET /api/services/{id} (JSON)

ServiceDetailViewModel — portal-internal view model

# Deliverable Status
C-65 model/ServiceDetailViewModel.java — Java record; portal-internal (not in apix-common); carries: all BSM payload fields; oLevelLabel (e.g. "Legal Entity Verified"); oLevelDescription (2-sentence plain-English, locale-resolved from Messages); oLevelColorClass (CSS class: trust-unverified/trust-basic/trust-strong); same pattern for sLevel*; livenessLabel, livenessColorClass, livenessExplanation; formattedUptime ("98.4%"), formattedAvgResponseMs ("142 ms"); registeredAtRelative, registeredAtIso; lastCheckedAtRelative, lastCheckedAtIso; gleifLeiUrl (null or https://search.gleif.org/#/record/{lei}); isReferenceEntry boolean [ ]
C-66 Update PortalResource.javaGET /services/{id}: call RegistryClient.getDetail(id), build ServiceDetailViewModel via ServiceDetailViewModelFactory, pass to service.html template [ ]
C-67 service/ServiceDetailViewModelFactory.java — CDI bean; receives ServiceDetailDto + Locale + injected Messages; resolves O-level description string by level index (keys: service.oLevel.0.descriptionservice.oLevel.5.description); computes relative timestamps via java.time; builds ServiceDetailViewModel [ ]

O-level description message keys (to be added to C-54 Messages.java + C-55/C-56 properties files):

Key EN value
service.oLevel.0.name Unverified
service.oLevel.0.description Self-declared only. No identity claims have been independently confirmed by APIX. Treat output with appropriate caution.
service.oLevel.1.name Identity Verified
service.oLevel.1.description The provider's domain ownership has been confirmed via DNS verification and their business email is reachable. APIX has confirmed this registrant controls the declared domain.
service.oLevel.2.name Legal Entity Verified
service.oLevel.2.description The provider's legal incorporation has been confirmed against the GLEIF global legal entity database or an authoritative company register. This is a real, registered legal entity — not self-declared.
service.oLevel.3.name Hygiene Verified
service.oLevel.3.description The provider publishes a security contact, enforces email authentication (DMARC/SPF), and maintains reachable legal documents. They meet baseline operational transparency standards.
service.oLevel.4.name Operationally Verified
service.oLevel.4.description An accredited APIX Verifier has assessed this provider's operational practices, incident response, and SLA track record. This level requires a human review process.
service.oLevel.5.name Audited
service.oLevel.5.description The provider holds a third-party security audit certificate (SOC 2 Type II or ISO 27001) confirmed by an APIX Accredited Verifier. The highest trust level available.

Same key pattern for service.sLevel.*. German equivalents go in messages_de.properties.

i18n — server-side message bundles (Quarkus @MessageBundle)

# Deliverable Status
C-54 Messages.java@MessageBundle interface; one method per translatable string key; sections: nav.*, home.*, register.*, search.*, service.*, admin.*, help.*, error.* [ ]
C-55 resources/i18n/messages.properties — English strings (all keys defined; this is the build-time default) [ ]
C-56 resources/i18n/messages_de.properties — German strings (same key set as EN) [ ]
C-57 LocaleResolver.java — CDI bean; reads apix-locale cookie first, then Accept-Language header, then falls back to Locale.ENGLISH; returns java.util.Locale [ ]
C-58 resource/LocaleResource.javaPOST /locale; validates lang param against ["en","de"]; sets apix-locale cookie (HttpOnly, SameSite=Lax, path /); redirects to Referer [ ]

Help system — overlay engine + tour content

# Deliverable Status
C-59 resources/META-INF/resources/help.js — client-side tour engine; spotlight four-wing dimming (help-dim-top/left/right/bottom) + highlight ring; draggable tour card (header grip); progress dots; state indicator; page-help drawer (slide-in right); context filter (shows only tours whose pages includes current <body data-page-id>); reads window.PAGE_TOURS + window.PAGE_HELP; no external dependency [ ]
C-60 templates/layout.html — Qute base layout extended by all portal pages; contains: nav bar with help button (?) and language switcher form; overlay HTML (4 wing divs + highlight ring + tour card shell + progress dots); help drawer HTML (guided tour list + page help section); <script src="/help.js">; all nav/chrome strings via {inject:msg.*} [ ]
C-61 model/TourDefinition.java + model/TourStep.java — Java records; TourDefinition: tourId, pages (list of page IDs), title, steps; TourStep: targetSelector (CSS selector), title, body, checkFn (optional JS function name for step validation) [ ]
C-62 service/HelpContentService.java — CDI bean; builds locale-resolved list of TourDefinition using injected Messages; serializes to JSON (Jackson); defines 5 MVP tours: tour-agent-setup (3 steps, home), tour-register (5 steps, register), tour-search (3 steps, search), tour-trust (4 steps, service detail), tour-admin (4 steps, admin) [ ]

Tests

Tests live in each module's src/test/. Library module tests are plain JUnit (fast). Quarkus module tests use @QuarkusTest with @QuarkusTestResource for Testcontainers (PostgreSQL).

apix-verification tests (plain JUnit 5 + WireMock)

# Deliverable Status
C-43 O1DnsVerifierTest — valid domain, missing TXT record, wrong value [ ]
C-44 O2GleifVerifierTest — LEI match, unknown LEI, timeout (WireMock HTTP server) [ ]
C-45 O3HygieneVerifierTest — security.txt present/absent, DMARC/SPF checks [ ]
C-46 SanctionsScreenerTest — match, no match, false positive (local CSV fixture) [ ]
C-47 VerificationPipelineTest — O-0 → O-3 happy path; blocked at O-2 (sanctions); O-3 failure preserves O-2 [ ]
C-48 LivenessEvaluatorTest — pure logic: 200+fast=LIVE, 200+slow=DEGRADED, 503=UNREACHABLE, timeout=UNREACHABLE [ ]

apix-registry tests (@QuarkusTest + Testcontainers + RestAssured)

# Deliverable Status
C-49 RegistryServiceTest — register happy path, duplicate UPSERT, invalid BSM rejected [ ]
C-50 ServiceResourceTest — capability search match/no-match/partial; HATEOAS root links present [ ]
C-51 RegisterResourceTest — valid registration 201, missing API key 401, invalid payload 400 [ ]

apix-spider tests (@QuarkusTest + WireMock)

# Deliverable Status
C-52 SpiderSchedulerTest — trigger one check cycle; verify liveness written to DB [ ]

apix-portal tests (@QuarkusTest + WireMock for registry)

# Deliverable Status
C-53 PortalResourceTest — homepage renders; search form submits; registration form renders [ ]
C-63 LocaleResolverTest — DE Accept-Language header → Locale.GERMAN; EN cookie overrides DE header → Locale.ENGLISH; absent header + absent cookie → Locale.ENGLISH default [ ]
C-64 HelpContentServiceTest — register page returns exactly 5 tour steps; home page tour excludes tour-admin; DE locale produces DE strings in tour title/body JSON; page filter returns only tours referencing current page ID [ ]
C-68 ServiceDetailViewModelFactoryTest — O-0 produces colorClass trust-unverified + EN description; O-2 with LEI → gleifLeiUrl non-null and correct; registeredAtRelative computed from fixed Instant; isReferenceEntry true when flag set; DE locale → DE description strings from properties [ ]

Infrastructure Deliverables

Docker / Compose — infra/

# Deliverable Status
I-01 docker-compose.yml — five services: registry (:8180), spider (:8082 internal), portal (:8081), db (postgres:16-alpine), caddy (:80/:443) [ ]
I-02 docker-compose.override.yml — dev overrides: JVM mode for all three Quarkus apps (quarkus dev); all ports exposed; no TLS; hot reload [ ]
I-03 Caddyfile — HTTPS auto-cert; /api/* → registry:8180; /* → portal:8081; spider has no public route [ ]
I-04 apix-registry/Dockerfile — multi-stage: GraalVM 21 Maven builder → UBI Minimal 8 runtime; non-root user; exposes 8180 [ ]
I-05 apix-spider/Dockerfile — multi-stage: GraalVM 21 Maven builder → UBI Minimal 8 runtime; non-root user; no exposed port (internal only) [ ]
I-06 apix-portal/Dockerfile — multi-stage: GraalVM 21 Maven builder → UBI Minimal 8 runtime; non-root user; exposes 8081 [ ]
I-07 .env.example — QUARKUS_DATASOURCE_JDBC_URL, API_KEY, GLEIF_API_URL, OPENCORPORATES_API_KEY, SANCTIONS_CACHE_PATH, SPIDER_INTERVAL, LOG_LEVEL, REGISTRY_BASE_URL (portal→registry) [ ]

Hetzner — infra/hetzner/apix-app/ (APIX application VPS)

# Deliverable Status
I-08 provision.sh — VPS bootstrap: Docker install + Swarm init (docker swarm init), firewall (80/443 only), swap, non-root user, Hetzner volume mount [ ]
I-09 backup.sh — pg_dump to Hetzner volume; retain 7 dumps; run via cron at 03:00 UTC [ ]
I-10 DNS setup notes — A record for registry.botstandards.org (or agreed domain) → apix-app VPS IP [ ]
I-11 sanctions/download.sh — download OFAC SDN, EU consolidated, UN SC sanctions lists; run weekly via cron [ ]
I-12 docker-stack.yml — production Swarm stack; all five services (registry, spider, portal, db, caddy); deploy.update_config.order: start-first; deploy.rollback_config; health check references; image tags pulled from Gitea registry [ ]

Hetzner — infra/hetzner/apix-gitea/ (Gitea VPS)

# Deliverable Status
I-13 provision.sh — Gitea VPS bootstrap: Docker install, firewall (22/80/443), swap, non-root user [ ]
I-14 docker-compose.yml — Gitea + Caddy on Gitea VPS; Gitea data volume; SQLite (no external DB) [ ]
I-15 gitea-config/app.ini — Gitea configuration: container registry enabled, Gitea Actions enabled, organisation bot-standards-foundation, domain gitea.botstandards.org [ ]
I-16 GitHub push mirror setup — configure Gitea push mirror to github.com/bot-standards-foundation/* for all repositories; triggered on every push to main [ ]
I-17 act_runner JVM install — Gitea Actions runner on Gitea VPS; Java 21 + Maven; handles Stage 1 (fast cycle) and Stage 3 (deploy) CI jobs [ ]
I-18 act_runner GraalVM install — Gitea Actions runner on Gitea VPS (or apix-app VPS off-hours); GraalVM 21; handles Stage 2 (native image build); configured to run max 1 concurrent native job [ ]
I-19 DNS setup notes — A record gitea.botstandards.org → Gitea VPS IP [ ]

CI/CD Pipelines — .gitea/workflows/

# Deliverable Trigger Status
I-20 ci-fast.yml — Stage 1: mvn verify (JVM); all module unit tests + @QuarkusTest; WireMock-based verification tests; ~35 min Every push [ ]
I-21 ci-native.yml — Stage 2: mvn package -Pnative per Quarkus module; Docker multi-stage build; @QuarkusIntegrationTest against native container; push to Gitea registry as <module>:main-<sha> Merge to main [ ]
I-22 deploy.yml — Stage 3: SSH to apix-app VPS; docker service update --image <new> apix_<service> for registry + spider + portal; poll /q/health until UP; fail + alert on timeout (Swarm auto-rollbacks) Git tag v* [ ]

Local Developer Scripts — scripts/

# Deliverable Status
I-23 setup-dev.sh — idempotent local setup: check/install Java 21 (SDKMAN prompt), Maven, Docker; copy .env.example.env if missing; start PostgreSQL container; run Liquibase migrations via Maven; print next steps [ ]
I-24 dev.sh — start all three Quarkus modules in dev mode concurrently; each in its own named tmux pane (or background with log files if tmux absent); prints URLs on start [ ]
I-25 `restart.sh [registry spider
I-26 stop.sh — stop all dev-mode Quarkus processes; stop PostgreSQL container; clean PID files [ ]
I-27 reset.sh — stop everything; drop and recreate local DB; re-run migrations; restart dev mode [ ]
I-28 `logs.sh [registry spider

Build Sequence

Develop in JVM mode (quarkus dev) throughout — fast reload, continuous testing. Native build only in Block 5 for the production image.

Block 1 — Multi-Module Foundation (Weeks 12)

  • C-00 (parent pom.xml — BOM, module declarations, plugin config)
  • Create five Maven modules: apix-common, apix-verification, apix-registry, apix-spider, apix-portal
  • Generate three Quarkus modules via code.quarkus.io (or mvn quarkus:create):
    • apix-registry: RESTEasy Reactive, Hibernate ORM + Panache, PostgreSQL JDBC, Liquibase, SmallRye Health, Quarkus Security
    • apix-spider: Quarkus Scheduler, Hibernate ORM + Panache, PostgreSQL JDBC, REST Client Reactive, SmallRye Health
    • apix-portal: RESTEasy Reactive, Qute, REST Client Reactive, SmallRye Health
  • C-01 to C-05 (apix-common: enums + DTOs)
  • C-20 to C-24 (Liquibase changelogs + registry application.properties)
  • I-01, I-07 (docker-compose skeleton + .env.example)
  • D-05 to D-08, D-34 to D-47 (constraints + ADRs)

Block 2 — Core Registry API (Weeks 34)

  • C-06 to C-12 (apix-verification: all verifiers + pipeline + config)
  • C-13 to C-19 (apix-registry: resources, services, model, repository)
  • C-43 to C-48 (apix-verification tests — plain JUnit, no Quarkus context needed)
  • C-49 to C-51 (apix-registry tests — @QuarkusTest + Testcontainers)
  • D-09 to D-11 (context + technical diagrams)

Block 3 — Spider Module (Weeks 56)

  • C-25 to C-32 (apix-spider: scheduler, fetcher, evaluator, parsers, entity, repository, config)
  • C-52 (SpiderSchedulerTest)
  • I-05 (spider Dockerfile — multi-stage native)
  • D-20 to D-23 (runtime view sequence diagrams)

Block 4 — Portal Module + i18n + Help System + Human-Readable Service Detail (Weeks 79)

  • C-33 to C-42 (apix-portal: resources, REST client, Qute templates, CSS, config)
  • C-65 to C-67 (ServiceDetailViewModel, PortalResource update, ServiceDetailViewModelFactory)
  • C-54 to C-58 (i18n: Messages interface with O/S-level description keys, EN/DE properties files, LocaleResolver, LocaleResource)
  • C-59 to C-62 (help system: help.js, base layout, tour model records, HelpContentService with 5 tours)
  • Update C-36 to C-40 (Qute templates: extend layout.html; replace hardcoded strings with {inject:msg.*}; add <body data-page-id="..."> attribute; inject PAGE_TOURS + PAGE_HELP script block via HelpContentService)
  • C-38 service.html: 7-section human-friendly layout (identity hero, trust card, liveness card, capabilities, pricing, contact, integration collapsible)
  • C-53 (PortalResourceTest)
  • C-63, C-64, C-68 (LocaleResolverTest, HelpContentServiceTest, ServiceDetailViewModelFactoryTest)
  • I-03 (Caddyfile)
  • I-06 (portal Dockerfile — multi-stage native)
  • D-52 to D-53 (ADR-013 i18n, ADR-014 help system)

Block 5 — Gitea + CI/CD Infrastructure (Weeks 910)

  • I-13 to I-19 (provision Gitea VPS, install Gitea + Caddy, configure container registry, GitHub mirror, act_runners)
  • I-20 (ci-fast.yml — Stage 1 pipeline; verify it passes on first push)
  • I-21 (ci-native.yml — Stage 2; first successful native build + push to Gitea registry)
  • I-23 to I-28 (local dev scripts: setup-dev, dev, restart, stop, reset, logs)
  • I-02 (docker-compose.override.yml — JVM dev mode for all three Quarkus modules)

Block 6 — Production Deployment + Zero Downtime (Weeks 1011)

  • I-08 to I-12 (provision apix-app VPS as Swarm node, backup, sanctions download, docker-stack.yml)
  • I-04 to I-06 (Dockerfiles for all three Quarkus modules — multi-stage native)
  • I-22 (deploy.yml — Stage 3; first tagged release deployed with zero-downtime verification)
  • D-24 to D-27 (deployment view — two-VPS diagram + Swarm rolling update sequence)
  • Fix any GraalVM reflection hints discovered during native integration tests

Block 7 — Arc42 Completion + Real Services (Weeks 12)

  • Remaining arc42 docs (D-01 to D-04, D-40 to D-46)
  • Register RS-01 to RS-08 (self + Lexnexum + reference registrations)
  • Manual outreach to founding member candidates (RS-09)

Real Services Target List

At least 5 live registered services required for a credible PoC. Candidates:

# Service Type Source
RS-01 APIX index itself (/health, /services) Self-registration Day 1
RS-02 Public OpenAPI test service (Petstore or equivalent) Demo Day 1
RS-03 BSF website bot endpoint Self-registration Week 4
RS-04 Lexnexum — legal search / legal portal bot endpoint innoit.de / lexnexum.ai — controlled registrant Week 4
RS-05 GLEIF API — legal-entity.lookup capability Reference registration by BSF; invite GLEIF to self-upgrade Week 5
RS-06 OpenCorporates API — company.lookup capability Reference registration by BSF Week 5
RS-07 EU Sanctions API (eu-sanctions.io or direct list endpoint) — sanctions.screen capability Reference registration by BSF Week 5
RS-08 Companies House UK API — org.verify.uk capability Reference registration by BSF Week 5
RS-09 Early adopter from founding member outreach Real Week 8+
RS-10 Developer community volunteer (HN / LangChain) Community Week 10+

Open Questions

# Question Owner Status
OQ-MVP-01 Domain name for public PoC — registry.apix.dev, index.botstandards.org, other? Carsten [ ]
OQ-MVP-02 GitHub organisation for open source repo — bot-standards-foundation or personal? Carsten [ ]
OQ-MVP-03 API key distribution for write access during PoC — how are early registrants onboarded? Carsten [ ]
OQ-MVP-04 Spider check frequency — every 15 min is reasonable for PoC; confirm acceptable load on registrant services Carsten [ ]
OQ-MVP-05 Hetzner server size — CX22 (2 vCPU, 4GB) sufficient for PoC; upgrade path if needed Carsten [ ]
OQ-MVP-06 OpenCorporates API key — free tier sufficient for PoC volume? Confirm rate limits before build Carsten [ ]
OQ-MVP-07 Sanctions list update cadence — weekly download of OFAC/EU/UN lists acceptable? Check if any list requires a licence for automated use Carsten [ ]
OQ-MVP-08 GLEIF API — confirm no API key required for public LEI lookup (currently free/open); check terms of use for automated batch use Carsten [ ]
OQ-MVP-09 Reference registrations (RS-05 to RS-08): BSF registers third-party APIs at O-0. Confirm BSF ToS allows this. Prepare outreach template inviting self-registration upgrade. Carsten [ ]
OQ-MVP-10 Gitea domain — gitea.botstandards.org or separate domain (e.g. code.apix.dev)? Confirm before provisioning Caddy TLS cert Carsten [ ]
OQ-MVP-11 GitHub org name — bot-standards-foundation exists? Create before configuring push mirror Carsten [ ]
OQ-MVP-12 Native build runner placement — on Gitea VPS or apix-app VPS? apix-app VPS risks resource contention during native build; Gitea VPS is cleaner but requires GraalVM installed there Carsten [ ]
OQ-MVP-13 Zero-downtime validation — how to confirm health check passes before declaring deploy successful in CI? Poll /q/health with retry loop or use Swarm service inspection Carsten [ ]