Athena — developer-portal/architecture.md

developer-portal — Architecture

Status: vision-stage; landed 2026-05-27 alongside the owner Option C / Phase-2 / Phase-3 addendum.

This document captures the architecture of the public developer hub for the Mahmoud Consultancy B.V. portfolio. It complements README.md (the what / why) by documenting the how — the three structural layers and how they survive the Growth path spin-out at phase 3.


Three structural layers

The portal is intentionally a thin composition of three layers, each independently swappable:

┌─────────────────────────────────────────────────────────────┐
│  1. Docs layer (Redoc + per-product sections)              │
│     — Public, read-only, static-rendered                   │
├─────────────────────────────────────────────────────────────┤
│  2. Spec ingestion pipeline                                 │
│     — Each product CI publishes OpenAPI; portal pulls      │
│       on release and rebuilds the docs site                │
├─────────────────────────────────────────────────────────────┤
│  3. Auth + key-issuance layer (per product)                 │
│     — Sign-in, account, API keys; keys stored per-product  │
│       so they migrate cleanly on phase-3 spin-out          │
└─────────────────────────────────────────────────────────────┘

1. Docs layer — Redoc + per-product sections

  • Renderer: Redoc (read-only, dense, branded). One bundle per product spec.
  • Layout: landing page → per-product section → endpoints. Each product section is a fully self-contained sub-tree; the landing layer is the only place where cross-product navigation lives.
  • Public: all docs pages are unauthenticated. No paywall, no sign-in wall.
  • Try-it-out: Redoc is read-only by design. Decision between Swagger UI / Stoplight Elements / hybrid is tracked in dp#18. Whichever wins is mounted per endpoint inside the product section, so it inherits that product's auth context (sandbox key).
  • Brand: per the addendum, Option C — a neutral "Developer Portal" wrapper with each product clearly named in its own section. Legal fine-print names Mahmoud Consultancy B.V. as controller. See dp#22.

2. Spec ingestion pipeline

Each product owns its OpenAPI spec. The portal is downstream — it never edits specs, it pulls and renders.

Per-product CI contract:

  1. Backend build emits openapi.json (Spring Boot already produces this via springdoc).
  2. On a tagged release (or every develop push), CI publishes the spec to a known, versioned location:
  3. Portal subscribes to "new spec for product X version Y" events and rebuilds its Redoc bundle for that section.

Why CI-driven, not git-submodule:

  • Specs change on every backend release; submodule bumps would be noise.
  • A product spin-out (phase 3) is a one-line config change in the portal: "for product X, pull from developer.<product>.com/specs/ instead of the workspace bucket."
  • Each product's spec is independently versioned and signed by its own CI, preserving provenance.

Versioning: the portal keeps the latest stable per product and links to older versions; deprecation timelines are documented per product. Migration guides live in each product's section, authored by that product's team.

3. Auth + API key layer (per product)

This is the most consequential design choice for phase 3.

  • Sign-in: single portal account (one identity across products) — implemented in dp#15.
  • API keys: issued and stored per product, not centrally. A developer signed into the portal selects a product and mints a key scoped to that product — see dp#16.
  • Validation: each product backend validates incoming keys via a shared middleware library (one issue per product repo: auditPic#130, claimio#142, europeLogin#115, mahmoud-consultancy#218, valideerleeftijd#95).
  • Storage: key records are owned by each product backend's own DB, not the portal's. The portal calls a product-side issuance endpoint; the product persists the key and returns the rendered secret once.

Why per-product storage:

  • On phase-3 spin-out the product's keys travel with the product DB. No central key vault to disentangle.
  • Per-product rotation/revocation policies don't compete for a shared rate-limit or KMS root.
  • Audit trails stay inside the product that produced them (relevant for per-product GDPR / NIS2 obligations).

The trade-off: signing in to the portal and minting a key are two round-trips (portal → product). Accepted — clean phase-3 ergonomics beat one fewer hop.


URL plan — Option C with phase-3 redirect

Owner-confirmed (addendum 2026-05-27): Option C. Phase-1 lives under a neutral developer hub; per-product paths are designed to redirect cleanly on phase-3 spin-out.

Phase 1 (today) — single neutral host:

developer.glorylabs.nl/                      → landing (Option C neutral brand)
developer.glorylabs.nl/auditpic/*            → auditPic section (docs, keys, try-it)
developer.glorylabs.nl/claimio/*             → claimio section
developer.glorylabs.nl/europelogin/*         → europeLogin section
developer.glorylabs.nl/interimplaza/*        → mahmoud-consultancy / InterimPlaza section
developer.glorylabs.nl/valideerleeftijd/*    → valideerleeftijd section

Phase 3 (per-product, on spin-out) — branded subdomain:

developer.glorylabs.nl/<product>/*           → 301 redirect → developer.<product>.com/*
developer.<product>.com/*                    → the spun-out product's own portal

Why this URL shape:

  • Every product section already lives under a stable per-product path prefix. On spin-out, the redirect is a one-line rule per product — no link-rot for existing integrations.
  • Bookmarks, OAuth callback URLs, and webhook configs that customers point at developer.glorylabs.nl/<product>/... keep working through the redirect for as long as we want to maintain it.
  • API keys don't live in the URL — they're per-product backend records — so a spin-out doesn't invalidate keys; only the docs/portal surface changes hostname.

What is NOT in the URL plan:

  • No per-product subdomains under glorylabs.nl at phase 1 (e.g. auditpic.developer.glorylabs.nl). The path-prefix form keeps the neutral Option C brand intact.
  • No mixing phase-1 and phase-3 hosts in cross-links. When a product spins out, its section is fully moved; the redirect from developer.glorylabs.nl/<product> is the only artefact left behind.

Phase mapping (how each layer survives a spin-out)

| Layer | Phase 1 | Phase 2 (positioning only) | Phase 3 (spin-out) | |---|---|---|---| | Docs renderer | Redoc on developer.glorylabs.nl/<product> | unchanged; section brand foregrounds "by GloryLabs" | Redoc re-hosted on developer.<product>.com; old URL 301s | | Spec ingestion | portal pulls from workspace CI | unchanged | portal config switched to pull from spun-out product's CI; or the new product runs its own portal copy | | Auth / keys | per-product issuance via portal sign-in | unchanged | per-product key store moves with the product; portal just stops offering that product in its switcher | | Legal | Mahmoud Consultancy B.V. controller | unchanged | new sub becomes controller for its product; portal ToS scopes shrink to remaining products |

The point: phase 2 is a CSS/copy change; phase 3 is a config + redirect change. Neither needs a portal rewrite.


Related

Reacties

Nog geen reacties