Commit Graph
51 Commits
Author SHA1 Message Date
mats 1decb5e53a go-auth: P3 interop captures — §13.5 TOTP + §14.4 WebAuthn golden fixtures
The remaining P3 tail is done:

- §13.5: three TOTP codes for the shared RFC secret computed by an
  independent implementation (Python stdlib, anchored to RFC 4226 App D
  counter 0 = 755224) at recorded wall-clock Unix seconds, pinned in
  platform/auth TestInteropCapturePythonTOTP — the Go code must produce
  identical codes at the same instants and VerifyWindow(0) must accept
  each at its capture time (cross-implementation agreement).
- §14.4: a full WebAuthn ceremony through real Chrome (navigator.credentials
  with a CDP virtual authenticator: ctap2/internal, resident key, UV,
  ES256) against a live go-auth instance — registration + passwordless
  login both completed via the RP handlers. The raw browser-produced
  blobs (clientDataJSON, attestationObject, authenticatorData, signature)
  are stored verbatim as a golden fixture and replayed by
  TestInteropCaptureChromeRegistration/Assertion: the parser must decode
  the real CBOR/COSE/authenticator-data, and the real ECDSA signature
  must verify over SHA-256(authData ‖ clientDataJSON) with the parsed key.

Plan doc §13.5/§14.4 updated with the recorded values; P3 row marked
complete. Both new suites green.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-09-01 14:49:15 +02:00
mats 7995fedbd3 more auth 2026-09-01 14:26:57 +02:00
mats fd9446632d Add go-auth P3/D6: TOTP 2FA, WebAuthn passkeys, UsersManager
The authenticator-app second factor (D4, plan §5.7): shared RFC 4226/6238
primitive in platform/auth/totp.go (±1 window, monotonic replay floor, §13
RFC vectors), per-user AES-GCM-wrapped secrets under AUTH_TOTP_WRAP_KEY,
hashed one-shot recovery codes, and the password → /totp challenge (or
recovery) → session browser flow with first-login enrollment (otpauth QR via
vendored qrcode-generator, two-code confirm, once-only recovery reveal).
AUTH_REQUIRE_TOTP defaults on; per-challenge throttling (R5). Covered E2E by
internal/oidc/totp_flow_test.go.

Passkeys (D6, plan §5.8): go-auth is its own WebAuthn RP — hand-rolled
CBOR/COSE subset parser (stdlib only), register/login ceremonies with
attestation:none, per-request RP ID + origin derived from the issuer host,
AOF-backed cred:<user>:<credID> store. The /admin UsersManager (invite,
disable, reset password, list/rename/delete passkeys, enroll in-browser) is
session + admin-role gated. WebAuthn vectors + ceremony negatives in
internal/webauthn + webauthn_flow_test.go.

Also: bare-host GET / now redirects to /login (the issuer root otherwise
404s in a browser).

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-09-01 11:42:06 +02:00
mats a5d00c9d7e cmsctl: persist a scoped API key for script auth
Add `auth api-key {set|clear}` and prefer a stored key over the device
token, so scripts authenticate with a permanent minted API key
(X-API-Key) instead of re-running the 12h device flow. Explicit flags
and env still win over persisted credentials. Also treat a zero-byte
config file as defaults (previously every command failed to parse), and
correct AGENTS.md, which claimed device tokens auto-refresh.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-29 14:52:50 +02:00
mats cb7967e419 commerce: add CLI and checkout session lifecycle\n\n🤖 Generated with Codebuff\nCo-Authored-By: Codebuff <noreply@codebuff.com> 2026-08-28 09:48:35 +02:00
mats 8627f0a26f more 2026-08-27 15:50:09 +02:00
mats 938d8d2edb bump 2026-08-27 13:23:31 +02:00
mats b16820bf62 Bump go-cart-actor: scheduling cluster service + booking-hold cart/checkout seam
Deploys cmd/scheduling as a 3-replica actor cluster (shared RWX commerce-data
event logs, gRPC mesh via POD_IP + actor-pool label, edge /api/scheduling
pass-through, fail-closed mcpauth on admin/MCP). cart/checkout get
SCHEDULING_SERVICE_URL so line removal releases holds; backoffice gets
SCHEDULING_URL for the admin proxy. Platform seam: order.Line.Properties
carries booking_* fields through order.created; scheduling.booking_confirmed/
cancelled event types. Compose + prep-dev-data wire the schedules dir.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-26 16:42:05 +02:00
mats 7f73d6885b Add cmsctl release workflow + makefile install targets
- platform/Makefile: 'cmsctl' build target (static CGO_ENABLED=0) and
  'install-cmsctl' to ~/.local/bin (BIN_DIR overridable)
- .gitea/workflows/cmsctl-release.yml: tag-driven (cmsctl-v*) Gitea
  release action building linux/darwin amd64+arm64 binaries + sha256s
2026-08-25 09:26:58 +02:00
mats 4337110ae2 platform/edgeconfig: publish-time nginx fragment seam + switchable reloaders
Implements steps 1-2 and the RBAC half of step 4 of
docs/plan-storefront-edge-config.md (storefront auth gating, generalised
to dynamic grain routes).

- platform/edgeconfig: Fragment/Reloader interfaces, atomic Writer
  (all-or-nothing flush), HTTPReloader (dev), NullReloader,
  KubeReloader (injected PodLister keeps platform stdlib-only).
  go vet clean; tests pass under -race.
- deploy/reloader-lib.sh + nginx-reloader.sh: reload handler
  (cksum -> nginx -t -> graceful reload) + CGI endpoint on :8082
  (pod IP only) + 60s fallback poll. Include-only risk model: main
  config never changes at runtime; broken fragments fail validation
  and the previous config keeps serving.
- k8s: reloader wired into the nginx Deployment, writable cms-output
  mount, fragment include line, SA/Role/RoleBinding for backoffice
  pod listing; backoffice uses the new ServiceAccount.
- compose/dev: same handler on stock nginx:alpine, EDGE_RELOAD_PROVIDER=http
  targeting nginx:8082 from backoffice.
- docs: plan-storefront-edge-config.md (supersedes earlier gating plans)
  and the fixed-zones variant kept for drift analysis.
2026-08-24 21:59:02 +02:00
mats 6be6aa891a otellog: slog bridge that records errors on the active span
Error/Info helpers emit a structured log record AND touch the active span
(status + stack trace), so failures show up as red markers in Jaeger/Tempo
with correlated log lines. No-op safe when no span is active. First consumer:
go-cart-actor cmd/order.
2026-08-24 16:11:44 +02:00
mats be9ecd538b abandoned-cart visibility: capture-mode campaigns + admin view
Bumps go-cart-actor (profile-link recovery-contact backfill, capture
email sender, /carts/abandoned read API) and backoffice (Abandoned
carts panel, storefront recovery-contact wiring). Compose shares
CAMPAIGN_DIR between cart service and backoffice and sets
CART_EMAIL_SENDER=capture for dev. Plan doc:
docs/plan-abandoned-carts-visibility.md.
2026-08-24 12:49:30 +02:00
mats 93cc736ea9 sagas and order fixes 2026-08-23 18:54:22 +02:00
mats ccb43808fa stuff 2026-08-23 13:43:40 +02:00
mats 5807132ac6 mcp 2026-08-22 22:33:18 +02:00
mats 9f578780bd update 2026-08-22 20:51:37 +02:00
mats a7d8b6107a smtp and ui 2026-08-22 11:05:16 +02:00
mats 93cfa123e4 cmsctl and docs 2026-08-22 10:12:22 +02:00
mats fea6eb51b1 feat(platform): add cmsctl and document newsletter and shell updates
Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-20 18:13:49 +02:00
mats 0f8ed97878 fix(auth): write the shared API-key store world-readable so other services see key changes
The key store holds only SHA-256 hashes + metadata (no plaintext keys) and is
read by other services in their own containers running as different non-root
uids. saveLocked wrote it 0600, so when the backoffice pod (running as root)
minted or revoked a key the file became root-owned 0600 — unreadable to the
ledger/cart services, freezing their mtime-reload snapshots: new keys 401'd
and revocations never took effect. Write 0644 so every reader reloads.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-17 21:53:37 +02:00
mats 7c2a34cc7a feat(shop-sales): ledger contracts + docs for consumer PSP sales tracking
platform/order + platform/event gain the sale.paid and refund.issued wire
contracts (per-rate VatBuckets, PSP provider, timestamps) that drive the
ledger's SHOP-/REFUND- vouchers (go-cart-actor 2b819bd). docs/status.md
records the shop-sales + PSP settlement stack (tickets 17 + 18); the
wayfinder ledger tracker gains the two closed tickets and their Decisions
entries. Bumps go-cart-actor + backoffice (PSP float stat).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-17 20:01:58 +02:00
mats 16655e8dbc feat(tracking): ship the shell rebuild — k8s replicas 3 + REDIS_ONLY + HPA, partitioned ingest live
- deploy/k8s/25-tracking-orders.yaml: SHELL-REBUILD gating removed (LIVE) —
  replicas 3 + RollingUpdate, actor-pool=tracking label + POD_IP/NAMESPACE,
  REDIS_ONLY=1, TRACKING_PARTITIONS=16, HPA 3..12
- deploy/k8s/00-namespace-config.yaml: tracking PVC note → rebuild live (mount
  vestigial, kept for rollback)
- deploy/docker-compose.yml: TRACKING_PARTITIONS=16 on the reader producer +
  tracking consumer (partitioned ingest in the dev stack too)
- deploy/k8s/build-push.sh: gocartactor named context for the tracking image
- platform: rabbit.PublishEventTo (caller routing key) + event.PartitionOf /
  PartitionRoutingKey (shared §8 fnv1a hash) + tests
- docs: plan-tracking-shell-rebuild.md (config write half, pod-watcher
  assignment, real-AOF tape gate, R6 re-measure), status.md, CONTEXT,
  event-sourcing-stance, platform-architecture-review, read-model-projections;
  scripts/load-test-coordinator.sh
- submodules: slask-tracking ecbf894 (shell rebuild), slask-finder 84f7ff7
  (§8 partition-routed producer)

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-17 14:41:17 +02:00
mats 698ef70ce1 feat(mcp): ledger tools bridged into the CMS assistant /mcp + submodule bumps
The backoffice commerce build now merges the ledger service's full MCP
tool surface (/ledger-mcp) into /mcp via MountLedgerMCP — a JSON-RPC
bridge that forwards tools/call to the ledger service (single-writer
AOF preserved), gated by the shared MCP_API_KEY. Compose backoffice now
sets LEDGER_URL=http://ledger:8080.

Submodule bumps:
- backoffice: ledger MCP bridge + device approve-page sign-in gating
- slask-finder: safe returnTo after OIDC login + dep tidy
- go-cart-actor / go-image-gen / go-redirector / slask-tracking: dep updates

Infra: k8s order CHROME_CDP_URL pinned to the headless-shell ClusterIP
(DNS-rebinding guard) + shared invoice-issuer-config ConfigMap for the
invoice PDF seller block; platform go.mod refreshed (otel direct, x/sys).
docs/status.md notes the ledger tools on /mcp.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-16 18:22:11 +02:00
mats 0423723d43 feat(auth): GrantInfo — expose pending grant scopes + remaining TTL
The approve UI needs to show what a user code asks for and how long it
is valid; GrantInfo returns (scopes, secondsLeft, ok) without consuming
the grant (consumption stays one-shot in Token).
2026-08-16 14:29:22 +02:00
mats 2f3ea99745 feat(auth): minted keys + device login for the admin API and MCP endpoints
platform/auth gains the LocalJWT (HS256 mint/verify, AUTH_TOKEN_SECRET,
disabled when unset), the RFC 8628 DeviceFlow (start/approve/poll with
requested-scope gating) and KeyStore.ReloadIfChanged (mtime-based, so a
freshly minted key works across processes without a restart). One shared
store at /commerce/apikeys.json (AUTH_APIKEYS_FILE) now backs the
backoffice admin gate, the CMS API and every MCP endpoint; AUTH_TOKEN_SECRET
lets the companion app's device flow mint bearer tokens the same surfaces
accept. Submodule bumps: backoffice (AuthGate + device endpoints + mcp
scope), go-cart-actor (mcpauth sources), go-graph-cms-v2 (shared store +
token chain).

Also lands the still-pending receipt-ocr sidecar (previous pass): the
PP-OCR ONNX inference service + download-ocr-models.sh + k8s
deployment/initContainer, wired into compose and the ledger RECEIPT_OCR_URL,
with the extractor pairing fix riding the go-cart-actor bump.

Deploy: AUTH_APIKEYS_FILE/AUTH_TOKEN_SECRET/AUTH_DEVICE_BASE_URL on
backoffice + cart/profile/order/ledger in compose and k8s, /device ingress
route, stack-secrets AUTH_TOKEN_SECRET optional ref, docs updated.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-16 12:48:03 +02:00
mats d74c8a0592 feat(ledger): deploy the bookkeeping service — compose/k8s, ingress, off-site backup volume, submodule bumps
- deploy: ledger service in docker-compose (ledger profile), k8s 27-ledger.yaml
  (StatefulSet, PVCs, OFFSITE_DIR backup volume), ingress route, nginx
  /api/ledgers proxy, ucp-profile with the ledger grain
- platform/event + platform/order: invoice.issued/paid/credited payload
  contracts (VatBuckets per rate, OCR reference on payments, credit-note
  reversal payload)
- prep-dev-data creates data/commerce/ledger
- docs/status.md: ledger service row + Phase-2 export/import summary;
  wayfinder: ledger-accounting map + tickets 07-16, b2b-invoices map notes
- bumps: go-cart-actor 5c9036d (ledger domain), backoffice f09202d
  (Ledger dashboard + proxy)

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-15 20:54:23 +02:00
mats 4bf00314d3 feat(event): add invoice.issued and invoice.paid event contracts 2026-08-14 08:24:08 +02:00
mats d6af4dbc0b tracking: TrackEvent platform type + custom-track island; bump submodules
- platform/event: TrackEvent (track.event) free-form storefront custom
  events, wired into KnownTypes + the drift-guard surface
- data/templates/header.gohtml: emit the custom-track island
- backoffice @ b4e6244 (cart upsell catalog caching, custom-track,
  round-2 editor polish)
- go-graph-cms-v2 @ e4cc6d1 (strip-extras failure reporting,
  custom-track island entry)
- slask-tracking @ ed18ed6 (custom-stats analysis + TrackEvent)
2026-08-11 17:35:11 +02:00
mats f1e6f1c44f tracking: promotion view/click event types + storefront beacons (submodule bumps backoffice, slask-tracking) 2026-08-11 15:32:41 +02:00
mats 4aa9c91bc7 newsletter: dev/deploy env wiring (PROFILE_URL, log sender), CampaignEvent.URL, plan + status docs 2026-08-05 15:07:39 +02:00
mats 0db9705063 some 2026-08-02 19:18:59 +02:00
mats 1c33ef4002 wishlist 2026-08-02 16:15:25 +02:00
mats d7f9688187 stuff 2026-07-26 21:53:08 +02:00
mats bd81f4ff54 update 2026-07-26 13:01:07 +02:00
mats 540aa48c44 more stuff 2026-07-25 18:13:22 +02:00
mats de2c822657 chore: add pre-commit hook to forbid committing root-level Go binaries 2026-07-25 16:39:30 +02:00
mats a53aba8b43 chore: pre-existing uncommitted platform/event changes 2026-07-25 15:25:04 +02:00
mats b155077d9b more 2026-07-21 00:03:47 +02:00
mats c4c78f6646 update 2026-07-20 00:15:42 +02:00
mats fc5eefbe5e more platform stuff 2026-07-18 23:28:51 +02:00
mats 4b9a882018 some improvements 2026-07-03 19:30:32 +02:00
mats 9390f2132d agents md 2026-07-03 18:14:28 +02:00
mats 8db09e3c6a test 2026-07-01 10:41:21 +02:00
mats d04679daf5 support datafields in projection 2026-06-30 10:31:31 +02:00
mats b128608316 better convert 2026-06-29 13:17:43 +02:00
mats 2a66754f9e more platform 2026-06-29 12:35:32 +02:00
matsandClaude Opus 4.8 8a71e32149 catalog: log ProjectionStore delta applies + greppable snapshot line
Restore the per-batch visibility lost in the cart migration: applyDelta now logs
'delta applied epoch=N (+U/-D, overlay=X)', and the snapshot swap line includes
'snapshot' so it's findable. Deltas were applying silently, which read as 'nothing
happened' on a bulk catalog update (only the debounced snapshot swap was logged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 21:42:46 +02:00
matsandClaude Opus 4.8 8b454bccac platform/catalog: generic ProjectionStore[T] consumer + race-free holder; finish C1
- Generic SnapshotWriter[T]/SnapshotReader[T] over a value-agnostic raw layer;
  SnapshotHolder[T] now RWMutex-guards munmap vs in-flight Get (race-free swap,
  TestSnapshotHolder_SwapRace) — the drain grace is now just close-batching.
- ProjectionStore[T]: snapshot begin|chunk|end assembly → atomic swap, epoch-gated
  delta overlay, tombstones, atomic epoch sidecar (ordered before the swap),
  cold-start reopen, mandatory ProjectionTransform[T]. Future-epoch deltas are
  dropped by contract (recover via next snapshot), not buffered.
- Shared bus-framing constants so producer + consumer agree.
- Bump slask-finder / go-cart-actor / backoffice pointers; refresh status.md +
  catalog-projection.md to the finished state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 21:07:39 +02:00
mats 5a3ffcb6bc ref for projection snapshots 2026-06-28 18:41:31 +02:00
mats 21bcb63440 platform 2026-06-28 17:52:49 +02:00