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>
This commit is contained in:
+571
@@ -0,0 +1,571 @@
|
||||
# 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 3–5, 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.java` — `GET /` HATEOAS root; navigation links JSON | `[ ]` |
|
||||
| C-14 | `resource/ServiceResource.java` — `GET /services` (capability search + filters), `GET /services/{id}` | `[ ]` |
|
||||
| C-15 | `resource/RegisterResource.java` — `POST /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.xml` — `services` 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.java` — `GET /`, `/services/{id}`, `/search`, `/register`; delegates to `RegistryClient` | `[ ]` |
|
||||
| C-34 | `resource/AdminResource.java` — `GET /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.java` — `GET /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.description` … `service.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.java` — `POST /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; ~3–5 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|portal|all]` — kill and restart the specified dev-mode process; defaults to `all`; reads PID file or tmux session name | `[ ]` |
|
||||
| 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|portal]` — tail logs for a specific dev-mode service; or `all` for multiplexed output | `[ ]` |
|
||||
|
||||
---
|
||||
|
||||
## 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 1–2)
|
||||
- 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 3–4)
|
||||
- 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 5–6)
|
||||
- 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 7–9)
|
||||
- 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 9–10)
|
||||
- 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 10–11)
|
||||
- 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 | `[ ]` |
|
||||
Reference in New Issue
Block a user