feat: 100% a8n.tools parity - SSR frontend, dunite backend, governance conformance #16

Merged
David merged 8 commits from feat/bunyip-dunite-infra-boot into main 2026-05-31 01:27:33 +02:00
Owner

Summary

Brings bunyip to 100% functional parity with a8n.tools (menkent), all on the generic dunite crates, and into full conformance with the governance standard. Backend = real actix-web SaaS + OpenID Provider; frontend = Axum SSR (Dioxus removed). Verified end-to-end. Supersedes this PR's original infra-only scope.

What's here

  • Backend (bunyip-domain + bunyip-oci + bunyip-oidc + bunyip-api): full menkent parity. bunyip-domain (renamed from bunyip-core to match menkent-domain) owns the domain on the generic dunite-core kernel; verticals consume dunite's generic OCI/OIDC engines; bunyip-api is the actix binary (replaced the axum mock); /version preserved.
  • Frontend (bunyip-web): Dioxus SPA removed; ported menkent/web (Axum 0.7 + Maud + htmx, cookie-forwarding BFF) rebranded PSA Systems, wired to bunyip's own /v1 API + OIDC issuer.
  • OIDC client: seed migration registering mokosh-server as a confidential relying party (placeholder dev redirect URIs + secret-at-deploy, a8n.tools client-seed pattern).
  • Governance conformance: justfile recipe set, .forgejo workflows (build-api + build-web + check + create-release), oci-build Dockerfiles on rust-builder-musl (api) / rust-builder-glibc (web) with builder/runtime/export + get-tags.nu, prod compose.yml, CLAUDE.md, LICENSE.md. License kept Proprietary (PSA Systems org SaaS), not MIT.
  • dunite consumed via git (anonymously readable; builds need no token).

Verification (host, against Postgres)

Migrations apply; api /v1/health 200; OIDC discovery + JWKS serve the real EdDSA key; /v1/auth/login authenticates the seeded admin; SSR web renders (/, /login, /pricing 200, "PSA Systems") talking to the api. Container builds validated via docker compose config.

Notes

  • License reverted to Proprietary (conformance pass had flipped it to MIT).
  • mokosh-server real redirect URIs + secret set at integration time (cross-repo).
  • Inert #[cfg(test)] fixtures retain a8n/rus placeholders.

Generated with Claude Code.

## Summary Brings bunyip to 100% functional parity with a8n.tools (menkent), all on the generic dunite crates, and into full conformance with the governance standard. Backend = real actix-web SaaS + OpenID Provider; frontend = Axum SSR (Dioxus removed). Verified end-to-end. Supersedes this PR's original infra-only scope. ## What's here - Backend (bunyip-domain + bunyip-oci + bunyip-oidc + bunyip-api): full menkent parity. bunyip-domain (renamed from bunyip-core to match menkent-domain) owns the domain on the generic dunite-core kernel; verticals consume dunite's generic OCI/OIDC engines; bunyip-api is the actix binary (replaced the axum mock); /version preserved. - Frontend (bunyip-web): Dioxus SPA removed; ported menkent/web (Axum 0.7 + Maud + htmx, cookie-forwarding BFF) rebranded PSA Systems, wired to bunyip's own /v1 API + OIDC issuer. - OIDC client: seed migration registering mokosh-server as a confidential relying party (placeholder dev redirect URIs + secret-at-deploy, a8n.tools client-seed pattern). - Governance conformance: justfile recipe set, .forgejo workflows (build-api + build-web + check + create-release), oci-build Dockerfiles on rust-builder-musl (api) / rust-builder-glibc (web) with builder/runtime/export + get-tags.nu, prod compose.yml, CLAUDE.md, LICENSE.md. License kept Proprietary (PSA Systems org SaaS), not MIT. - dunite consumed via git (anonymously readable; builds need no token). ## Verification (host, against Postgres) Migrations apply; api /v1/health 200; OIDC discovery + JWKS serve the real EdDSA key; /v1/auth/login authenticates the seeded admin; SSR web renders (/, /login, /pricing 200, "PSA Systems") talking to the api. Container builds validated via docker compose config. ## Notes - License reverted to Proprietary (conformance pass had flipped it to MIT). - mokosh-server real redirect URIs + secret set at integration time (cross-repo). - Inert #[cfg(test)] fixtures retain a8n/rus placeholders. Generated with Claude Code.
The binary ported from menkent never called dotenvy::dotenv(), so a local .env was ignored and config fell back to production defaults (panicking on the required encryption keys). Adds `let _ = dotenvy::dotenv();` as the first line of main() - a no-op in containers that inject real environment, a convenience for host `cargo run` dev.

Verified end-to-end against a Postgres: migrations apply on a fresh DB, /v1/health 200, /.well-known/openid-configuration + jwks.json serve the real EdDSA key, and /v1/auth/login authenticates the SETUP_DEFAULT_ADMIN seed.

Note for .env authors: dotenvy 0.15 needs quotes around values containing spaces (APP_NAME="PSA Systems"); an unquoted spaced value errors the whole file.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(infra): wire dev + prod stack for the actix backend on dunite
Some checks failed
build / Build and push OCI images (pull_request) Has been skipped
build / Lint and type-check (pull_request) Failing after 4s
9ee7a3b75e
Rewires the container infra now that bunyip-api is the actix backend consuming the private dunite git crates.

compose.dev.yml: the `api` service runs the actix binary against the `postgres` service (DATABASE_URL over the compose network), mounts ./secrets at /run/secrets/oidc for the OIDC Ed25519 keys, sets the full dunite env (APP_NAME, OIDC_ISSUER, JWT/encryption secrets, SQLX_OFFLINE), depends_on postgres healthy, and drops all the mock env (BUNYIP_SEEDS_DIR, MOCK_*, COOKIE_SECRET). Postgres is no longer "idle scaffold".

bunyip-api/Dockerfile (dev): rust + cargo-watch + git; fetches the private dunite dep via CARGO_NET_GIT_FETCH_WITH_CLI using DUNITE_GIT_TOKEN (git insteadOf in the entrypoint); SQLX_OFFLINE; cargo-watch on bunyip-api + crates.

bunyip-api/oci-build/Dockerfile (prod): the git dep collapses the build context back to the repo root (no more sibling-checkout). musl-static build with a buildkit secret `dunite_token` for git auth; binary embeds migrations (sqlx::migrate!) and email templates (include_str!), so the runtime image is just the static binary. Health on /v1/health:4401.

.dockerignore added (keeps target/, .git, secrets/, .env out of context). .env.example dev section rewritten for the dunite backend (Postgres, secrets, OIDC keygen, DUNITE_GIT_TOKEN); the SPA + self-host sections are preserved.

Verified: the binary boots on the host against Postgres (migrations apply, /v1/health, OIDC discovery + JWKS with real EdDSA, /v1/auth/login). The container images are configured but their builds require a DUNITE_GIT_TOKEN (private dep) and are not yet CI-verified.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: a8n.tools (menkent) parity - SSR frontend, governance conformance, domain rename
Some checks failed
Check / fmt / clippy / build / test (pull_request) Failing after 6s
2e5ee273e9
Brings bunyip to full functional parity with the a8n.tools implementation (menkent), all built on the generic dunite crates, and into conformance with the governance standard.

Frontend (remove Dioxus -> Axum SSR): deletes the Dioxus SPA and ports menkent/web (Axum 0.7 + Maud + htmx, a cookie-forwarding BFF) into bunyip-web, rebranded PSA Systems, pointed at bunyip's own /v1 API and OIDC issuer. Full page/flow parity (public, auth, dashboard, admin); Tailwind v4 via bun.

Backend parity + rename: the bunyip domain crate is renamed bunyip-core -> bunyip-domain to match menkent-domain. bunyip-domain consumes the generic dunite-core kernel; bunyip-oci/oidc consume dunite's generic OCI/OIDC engines; bunyip-api is the actix binary. Adds a seed migration registering mokosh-server as a confidential OIDC relying party (placeholder dev redirect URIs + NULL secret to set at deploy, matching the a8n.tools client-seed pattern).

Governance conformance (../governance): full justfile recipe set, .forgejo workflows (build-api + build-web + check + create-release), oci-build Dockerfiles on the rust-builder-musl (api) / rust-builder-glibc (web) base images with builder/runtime/export stages + get-tags.nu, prod compose.yml (published images + postgres, HOST_IP/APP_PORT), CLAUDE.md, LICENSE.md, .dockerignore. License kept Proprietary (PSA Systems org SaaS), not MIT.

Verified end-to-end on the host against Postgres: migrations apply, api /v1/health 200, OIDC discovery + JWKS serve the real EdDSA key, auth login works, and the SSR web renders (landing/login/pricing 200, "PSA Systems" branding) talking to the api.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David changed title from feat: boot-verify the dunite backend + wire dev/prod container infra to feat: 100% a8n.tools parity - SSR frontend, dunite backend, governance conformance 2026-05-30 21:13:11 +02:00
chore: MIT license, secret-generating ensure-env, justfile groups, startup banners
Some checks failed
Check / fmt / clippy / build / test (pull_request) Failing after 14s
c1e9a2107a
- License: switch to MIT (all code is open source) - Cargo.toml + LICENSE.md.
- justfile ensure-env: port menkent's secret-generating recipe - creates .env from the example and fills TOTP_ENCRYPTION_KEY / STRIPE_ENCRYPTION_KEY / JWT_SECRET with random 32-byte hex (the api panics on non-hex keys), leaving an existing .env untouched.
- justfile grouping: add [group: ...] attributes (dev / local / checks / database / images / release) so `just --list` is grouped.
- dev-clean: now also deletes the generated .env (matching menkent), not just the named volumes.
- Startup banners: bunyip-api and bunyip-web each print a banner after binding, stating the address they listen on (api also shows the OIDC issuer + OCI port when enabled; web shows its API backend URL).

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(web): serve assets/styles.css (fix 404, broken styling)
Some checks failed
Check / fmt / clippy / build / test (pull_request) Failing after 4s
7517a11aa2
The SSR server uses ServeDir("assets") (CWD-relative), but the dev container CMD built the CSS in /app/bunyip-web then `cd /app` and ran cargo from the repo root, so /assets/styles.css resolved to /app/assets (empty) -> 404, no styling.

- Dev Dockerfile: stay in /app/bunyip-web when running cargo-watch so the CWD-relative ServeDir("assets") finds the crate's assets/ (cargo still locates the workspace by walking up).
- just run-web: cd into bunyip-web for the same reason on host runs.
- Commit bunyip-web/assets/styles.css (un-ignore it), matching menkent, so host runs / fresh clones serve styling without a build step (the dev container + prod image still rebuild it via `bun run build:css`).

Verified: GET /assets/styles.css -> 200 text/css (52 KB), landing references it.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(web): restore Bunyip styling + wording (reed/water theme, "Surfaces what matters")
Some checks failed
Check / fmt / clippy / build / test (pull_request) Failing after 5s
c1b6691610
The SSR port inherited menkent's (a8n.tools) shadcn/orange theme and "PSA Systems / Managed tools" copy. Restores Bunyip's own brand identity, recovered from the original Dioxus frontend.

- Theme (input.css): remap the shadcn semantic tokens (background/foreground/primary/secondary/muted/card/border/ring/...) to Bunyip's reed/water green palette so every existing template restyles with no class churn; add the bunyip-reed-* / bunyip-water-* + surface @theme tokens used by the restored landing; Inter + JetBrains Mono fonts; dark + high-contrast variants. styles.css regenerated (reed #2f4e2e, no orange f97316).
- Wording: brand "Bunyip" + tagline "Surfaces what matters." in nav/footer; meta description + theme-color metas; landing hero ("The SaaS layer for your PSA"), six feature cards (SSO/billing/orgs/MFA/admin/feedback), and CTAs restored from the original; our-story/pricing/feedback reframed to Bunyip's business-shell-for-Mokosh framing; page-title suffixes "· Bunyip"; startup banner.

No handler logic, routes, or API-client code changed. Verified: cargo check -p bunyip-web clean; the running web container rebuilt styles.css in-container; landing serves Bunyip copy + reed theme.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(web): restore the Bunyip mascot illustration in the hero
Some checks failed
Check / fmt / clippy / build / test (pull_request) Failing after 15s
4f5bfd31db
The styling/wording restore brought back the copy but dropped the original hero illustration. Re-adds the BunyipMascot SVG (the lake creature peering through reeds over a water disc, with the "Surfaces what matters." caption) ported from the original Dioxus frontend, and makes the hero a 2-column grid (copy + mascot) like the original. Regenerated styles.css to include the water-gradient + aspect-square utilities.

Verified: landing serves the viewBox="0 0 400 400" mascot + caption; cargo check -p bunyip-web clean.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(web): tolerate missing updated_at in API responses (register/login 'missing field')
Some checks failed
Check / fmt / clippy / build / test (pull_request) Failing after 5s
Create release / Create release from merged PR (pull_request) Has been skipped
b51a15b572
The API's UserResponse (and the application/tier responses) serialize created_at but not updated_at, yet the web BFF's User / Application / TierConfigResponse structs required `updated_at: String`. serde then failed every register/login/me parse with "missing field `updated_at`", surfacing as a form error (the same bug menkent had).

updated_at is never read in the web, so mark those three fields `#[serde(default)]` (missing -> ""); the BFF should follow the API's actual contract, not over-specify it. serde already ignores the extra fields the API does send (grace_period_end, last_login_at).

Verified: POST /register -> 303 /dashboard (session established), no deserialization error; the API register response confirmed to omit updated_at.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David merged commit c65075c281 into main 2026-05-31 01:27:33 +02:00
David deleted branch feat/bunyip-dunite-infra-boot 2026-05-31 01:27:33 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!16
No description provided.