Registry: new GET /services/browse endpoint — paginated, no auth required,
returns ServiceBrowsePage (total, page, size, items). ServiceSummaryDto
extended with description, registrantName, registrantJurisdiction; lastCheckedAt
removed (not stored). ServiceBrowsePage added to apix-common as shared type.
Portal: ServiceBrowserResource at /browse (list + /browse/{id} detail);
ServiceRegistryClient for the new endpoints; PortalServiceView DTO for detail
deserialization; three Qute templates (browse, detail, error) in the existing
dark-theme design. Landing page links to /browse in the "Try it" section.
Architecture: registry stays pure JSON API; portal is the HTML layer over the
same data — two subdomains, no content negotiation required.
Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
- Sandbox search without ?stage= now defaults to DEVELOPMENT (was PRODUCTION),
so freshly registered services are discoverable by default
- DEPRECATED and DECOMMISSIONED stages are rejected at creation with 400;
they are lifecycle-only transitions, not valid initial states
- PRODUCTION is accepted at creation for established services going live immediately
BDD coverage: sandbox-stage.feature (6 scenarios, all green)
Fixes 2 pre-existing failures in sandbox-service-isolation.feature.
Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
Test first: sandbox-admin-list.feature verifies createdAt, expiresAt,
unauth rejection, and admin delete — three new BDD scenarios.
Bug found: listAllSandboxes() native query mapping used
instanceof java.sql.Timestamp, but Hibernate 6 + PostgreSQL JDBC
returns LocalDateTime for timestamptz columns. toInstant() now
handles Timestamp, OffsetDateTime, ZonedDateTime, LocalDateTime (UTC),
Date, and any Temporal via Instant.from() fallback.
UI change (admin list "Created" column, prior commit) now has
test coverage at the API layer.
Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
Bug 1 — sandbox-scoped discovery returns empty:
GET /services?capability=nlp with a sandbox API key returns [] because
Bunny.net CDN caches the public (PRODUCTION-scoped) empty response and
serves it regardless of the X-Api-Key header. Fix: ServiceResource.search()
now returns Cache-Control: private, no-store when a sandbox key is active,
opting the response out of CDN caching.
Bug 2 — _links.sandbox href uses sandbox name instead of UUID:
IndexResource.index() used sandbox.name as the URL path segment instead of
sandbox.id. Name-based URLs return 404 since migration-020 made UUID the
sole resource identifier. Fix: sandbox.name → sandbox.id.
Also: SandboxResource GET /{uuid} simplified to SandboxIndexResponse
(metadata + links only); heavy usage stats + agent visits are on
/telemetry. SandboxService feedback INSERT uses CAST(:x AS jsonb) for
portability across PostgreSQL JDBC driver versions.
Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
Three root causes fixed:
1. DeviceCucumberIT — duplicate step class removed from bdd.device; moved
to dedicated devicebdd package with its own TestSetup and glue scanning,
preventing duplicate-step conflicts with IoT suites.
2. DeviceNavigationSteps — ?-in-Cucumber-expression treated as optional-char
quantifier. Changed GET /devices?capability=, ?deviceClass=, ?protocol=
patterns to anchored regex (^…$) with (.+) capture so literal ? matches.
3. IotProfileCucumberIT iotReady=true — CanonicalQueryFilter was silently
redirecting ?iotReady=true to the canonical URL (which omitted iotReady).
RestAssured followed the 302 and the server never saw the parameter.
Fix: add iotReady to QueryNormalisationService.canonicalForReplacements
with normaliseIotReady ("true" → "true", else absent).
ServiceResource now reads iotReady via UriInfo (bypasses JAX-RS @QueryParam
which is downstream of the filter rewrite). RegistryService uses primitive
boolean + INNER JOIN iot_profiles when iotReady=true.
Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
apix-demo, apix-portal/src, apix-spider/src, apix-registry/src,
apix-common/src were never staged. Without them the CI build has no
source to compile and the Docker images cannot be produced.
Also adds docs/ (infrastructure notes) missed in prior commits.
Co-Authored-By: Mira <noreply@anthropic.com>
- .gitea/workflows/deploy.yml — push-to-main triggers rolling deploy
- scripts/deploy-bluegreen.sh — a-stack then b-stack restart; Maven runs
in Docker (no JDK needed on runner host); Caddy reload at end
- scripts/deploy-all.ps1 — emergency manual deploy from dev machine
- infra/docker-compose.yml — a/b pairs per service; wget health checks;
Gitea service; Prometheus/Grafana/DB ports restricted to localhost
- infra/Caddyfile — dual upstreams with health-based routing
- infra/Dockerfile.* — one per service
- infra/prometheus.yml + grafana provisioning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>