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:
Carsten Rehfeld
2026-05-08 09:13:26 +02:00
commit b2a16a8be7
71 changed files with 5480 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
---
arc42: "7 — Deployment View"
status: stub
---
## 7.1 Hetzner Deployment Diagram
```plantuml
@startuml deploy
node "Hetzner CX22\n(2 vCPU, 4GB RAM, Ubuntu 24.04)" as hetzner {
component [Caddy\n:80, :443] as caddy
component [API Service\n:8000 (internal)] as api
component [Portal Service\n:8001 (internal)] as portal
component [Spider Service\n(no exposed port)] as spider
database [PostgreSQL 16\n:5432 (internal)] as db
folder "Hetzner Volume\n(20GB)" as vol
}
cloud "Internet" {
actor Agent
actor Registrant
}
cloud "Let's Encrypt" as le
Agent --> caddy : HTTPS :443
Registrant --> caddy : HTTPS :443
caddy --> api
caddy --> portal
caddy <--> le : ACME cert renewal
api --> db
spider --> db
db --> vol : data persistence
@enduml
```
## 7.2 Environment Table
| Setting | Dev | Prod (Hetzner) |
|---|---|---|
| TLS | None (HTTP only) | Auto via Caddy + Let's Encrypt |
| DB | postgres:16 local container | postgres:16 container, data on Hetzner volume |
| Spider interval | 2 min (fast feedback) | 15 min |
| API key | `dev-key-insecure` | Strong random key, env var only |
| Log level | DEBUG | INFO |
| Port exposure | All ports exposed to host | Only :80, :443 via Caddy; all others internal |
## 7.3 Backup and Restore
- `backup.sh` runs via cron daily at 03:00 UTC
- Executes `pg_dump` into `/backup/apix_$(date +%Y%m%d).sql.gz`
- Backup directory mounted on Hetzner volume (separate from DB data volume)
- Retain last 7 dumps; older files deleted by script
- Restore: `psql < apix_YYYYMMDD.sql.gz` — documented in `infra/hetzner/RESTORE.md`
## 7.4 Domain and DNS
TODO: Confirm domain name (OQ-MVP-01).
Planned DNS setup:
- `registry.apix.dev` (or `index.botstandards.org`) → Hetzner VPS IP (A record)
- TTL: 300s initially for fast propagation during setup
Caddy will automatically obtain and renew the TLS certificate once the A record resolves to the server IP.