feat: scaffold bunyip-core/oci/oidc crates for the dunite rebuild #15

Merged
David merged 9 commits from feat/bunyip-api-on-dunite into main 2026-05-30 19:57:19 +02:00
Owner

Summary

Scaffold-only first step toward rebuilding bunyip-api from an axum in-memory mock into a real actix-web SaaS backend that is the OIDC provider (mokosh-server and other relying parties become clients), built on the generic dunite kernel and mirroring the menkent reference consumer. No domain code is ported yet; the existing mock, bunyip-web, README, and .env.example are untouched and the app still builds and runs.

Tracking: BUNYIP-26.

Why scaffold-only

Both sibling repos were actively mid-refactor while this was written, changing between edits:

  • dunite is on step 2/4 of a library-only refactor. dunite-core still ships the full fat domain layer (slated for removal in steps 3/4); dunite-oidc is not yet generic-ized; dunite-oci already is.
  • menkent is renaming menkent-domain to menkent-core and, per that crate's manifest, dropping the dunite dependency entirely ("Owned wholesale by menkent (no dunite dependency)").

That last point contradicts the directive for Bunyip to consume dunite as a generic library. The consumption-boundary question (consume dunite vs own wholesale) is deliberately left open until upstream settles. Full context: dev-docs/bunyip-on-dunite-scaffold.md.

What this PR adds

  • Empty crates/bunyip-{core,oci,oidc} skeletons mirroring menkent-{core,oci,oidc}, added to the workspace.
  • dunite-* wired as optional path deps behind a dunite feature (off by default), so the workspace compiles independently of upstream churn. cargo check -p bunyip-core -p bunyip-oci -p bunyip-oidc is green.
  • The 53 SQL migrations vendored from menkent/api/migrations into bunyip-api/migrations/ (unpruned; the 6 a8n domain seeds are flagged for fill-time removal).
  • An idle postgres:16-alpine dev service (dev-bunyip-postgres-${USER}) in compose.dev.yml, ready for the actix backend.

Not in this PR (fill steps)

Resolve the consume-vs-own decision, port the domain layer + verticals, convert bunyip-api to the actix binary, prune migrations + add Bunyip's own seeds, generate dev OIDC keys, finish dev/prod build infra, retire the mock. Enumerated in dev-docs/bunyip-on-dunite-scaffold.md.

🤖 Generated with Claude Code

## Summary Scaffold-only first step toward rebuilding `bunyip-api` from an axum in-memory mock into a real actix-web SaaS backend that **is the OIDC provider** (mokosh-server and other relying parties become clients), built on the generic dunite kernel and mirroring the `menkent` reference consumer. No domain code is ported yet; the existing mock, `bunyip-web`, README, and `.env.example` are untouched and the app still builds and runs. Tracking: BUNYIP-26. ## Why scaffold-only Both sibling repos were actively mid-refactor while this was written, changing between edits: - **dunite** is on step 2/4 of a library-only refactor. `dunite-core` still ships the full fat domain layer (slated for removal in steps 3/4); `dunite-oidc` is not yet generic-ized; `dunite-oci` already is. - **menkent** is renaming `menkent-domain` to `menkent-core` and, per that crate's manifest, **dropping the dunite dependency entirely** ("Owned wholesale by menkent (no dunite dependency)"). That last point contradicts the directive for Bunyip to consume dunite as a generic library. The consumption-boundary question (consume dunite vs own wholesale) is deliberately left open until upstream settles. Full context: `dev-docs/bunyip-on-dunite-scaffold.md`. ## What this PR adds - Empty `crates/bunyip-{core,oci,oidc}` skeletons mirroring `menkent-{core,oci,oidc}`, added to the workspace. - `dunite-*` wired as **optional path deps** behind a `dunite` feature (off by default), so the workspace compiles independently of upstream churn. `cargo check -p bunyip-core -p bunyip-oci -p bunyip-oidc` is green. - The 53 SQL migrations vendored from `menkent/api/migrations` into `bunyip-api/migrations/` (unpruned; the 6 a8n domain seeds are flagged for fill-time removal). - An idle `postgres:16-alpine` dev service (`dev-bunyip-postgres-${USER}`) in `compose.dev.yml`, ready for the actix backend. ## Not in this PR (fill steps) Resolve the consume-vs-own decision, port the domain layer + verticals, convert `bunyip-api` to the actix binary, prune migrations + add Bunyip's own seeds, generate dev OIDC keys, finish dev/prod build infra, retire the mock. Enumerated in `dev-docs/bunyip-on-dunite-scaffold.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(scaffold): skeleton bunyip-core/oci/oidc crates for the dunite rebuild
Some checks failed
build / Build and push OCI images (pull_request) Has been skipped
build / Lint and type-check (pull_request) Failing after 4s
23ede7cec0
Sets up the structural skeleton for rebuilding bunyip-api as a real actix-web SaaS backend and OIDC provider on the generic dunite kernel (mirroring the menkent reference consumer), without porting any domain code yet. The existing axum mock, bunyip-web, README, and .env.example are untouched and the app still builds/runs.

Why scaffold-only: both sibling repos are actively mid-refactor. dunite is on step 2/4 of a library-only refactor (dunite-core still ships fat domain code slated for removal in steps 3/4; dunite-oidc not yet generic-ized). menkent is renaming menkent-domain to menkent-core and, per that crate's manifest, dropping the dunite dependency entirely - which contradicts the directive for bunyip to consume dunite as a generic library. Resolve that consumption-boundary question once upstream settles. Full context in dev-docs/bunyip-on-dunite-scaffold.md.

What this adds: empty crates/bunyip-{core,oci,oidc} mirroring menkent's layout, added to the workspace; dunite-* wired as optional path deps behind a `dunite` feature (off by default) so the workspace compiles independently of upstream churn (cargo check on the three crates is green); the 53 SQL migrations vendored from menkent/api/migrations into bunyip-api/migrations (unpruned - the 6 a8n domain seeds are flagged for fill-time removal); and an idle postgres:16-alpine dev service ready for the actix backend.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(core): fill bunyip-core domain layer on the dunite-core kernel
Some checks failed
build / Build and push OCI images (pull_request) Has been skipped
build / Lint and type-check (pull_request) Failing after 6s
057ff502c5
Ports menkent-core's domain layer (config, all models, repositories, business services, and domain middleware: auth extractors + auto-ban) into bunyip-core, and re-points the generic kernel pieces to consume dunite-core instead of owning them. This is the foundation for rebuilding bunyip-api as a real actix-web SaaS backend on the now-generic dunite crates.

bunyip-core re-exports dunite-core's kernel under the original module names (errors, responses, validation, middleware::{request_id, security_headers}, services::{encryption, password, JwtConfig}) so the ported domain sources keep using crate::errors, crate::responses, crate::services::encryption, etc. unchanged. The kernel source files dunite provides were deleted; JwtService is kept locally and built on dunite-core's JwtConfig (the same type dunite-oci's OciTokenService consumes). Email Tera templates are vendored under crates/bunyip-core/templates for the compile-time include_str!.

The kernel reconciliation was clean (dunite-core is the common ancestor of menkent-core's kernel): bunyip-core compiles with zero errors. bunyip-oci/oidc manifests updated to depend on the generic dunite-oci/dunite-oidc engines (their source ports are next).

Domain branding (a8n.tools defaults: cache dirs, lifecycle-event URN, issuer examples) is NOT yet rebranded - that lands with the config pass.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switches bunyip-core/oci/oidc from local path deps on ../dunite to the published git source at https://dev.a8n.run/psa-systems/dunite (branch main). Cargo.lock pins the exact rev. This removes the side-by-side checkout requirement for builds (the prod Docker context no longer needs the sibling dunite repo mounted).

Adds .cargo/config.toml with net.git-fetch-with-cli = true so cargo authenticates to the private Forgejo repo via the git credential helper instead of libgit2.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the OCI registry vertical on top of dunite-oci's generic, storage-agnostic engine (mirroring menkent-oci for the consumer-side wiring, but without duplicating the engine).

The two repositories (oci_blob_cache, oci_pull_daily_counts) implement dunite-oci's store::BlobStore / store::PullCounter traits over Postgres, keeping menkent's SQL. Handlers, middleware (OciBearerUser extractor, WWW-Authenticate), and routes are ported and rewired to dunite-oci's generic engine types: BlobCache<OciBlobCacheRepository>, ManifestCache, OciLimiter (acquire threads a concrete PullCounter), OciTokenService, ForgejoRegistryClient. lib.rs is a shim re-exporting bunyip-core's domain plus dunite-oci's engine so the ported sources keep their crate:: paths.

Dropped menkent-oci's duplicated engine (services/*, models/oci, errors/oci) in favour of dunite-oci's, and the DB-gated full-stack integration test block that assumed the old concrete BlobCache (would need a rewrite against the generic engine). cargo check -p bunyip-oci is clean.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(oidc): port bunyip-oidc provider on dunite-oidc keys
Some checks failed
build / Build and push OCI images (pull_request) Has been skipped
build / Lint and type-check (pull_request) Failing after 42s
23fd1947ca
Implements the OpenID Provider vertical: ports menkent-oidc's provider (OidcProvider/OAuthClient, auth-code/token/session/lifecycle logic), handlers (discovery, jwks, authorize, token, userinfo, revoke, RP-initiated logout), and routes, consuming dunite-oidc's OidcKeySet for the Ed25519 signing keys + JWKS (menkent's own oidc_keys module is dropped). lib.rs is a shim re-exporting bunyip-core's domain plus dunite-oidc's keys.

The provider's sqlx::query! macros are validated against the committed .sqlx/ offline cache (vendored from menkent; SQL ported byte-for-byte so the content hashes match). Adds SQLX_OFFLINE=true to .cargo/config.toml so the whole workspace builds the macros offline. ACR identifier rebranded urn:a8n:loa:pwd -> urn:bunyip:loa:pwd (greenfield, no existing tokens). cargo check -p bunyip-oidc is clean.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(api): convert bunyip-api from axum mock to the actix binary 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 6s
6cd6d925e1
Replaces the axum in-memory mock with the real actix-web binary, ported from menkent/api and wired to bunyip-core (domain) + bunyip-oci + bunyip-oidc, which build on the generic dunite crates. This completes the backend: the full workspace (core + oci + oidc + api) compiles.

main.rs ports menkent's app assembly: config, pool, sqlx::migrate!, optional SETUP_DEFAULT_ADMIN seed, all service init (jwt/auth/email/encryption/stripe/forgejo/totp/webhook/OIDC-provider/auto-ban), CORS, background cleanup tasks, the primary HttpServer, and the conditional second OCI HttpServer (tokio::try_join). Routes mount the /v1 scope plus the root-level OIDC well-known/oauth2 routes from bunyip-oidc; the OCI server uses bunyip-oci's routes + WWW-Authenticate middleware.

Because dunite-oci's BlobCache<S> is generic over a BlobStore, main.rs constructs BlobCache<OciBlobCacheRepository> and registers OciPullDailyCountRepository as the PullCounter (a divergence from menkent's concrete, pool-backed cache). Bunyip's /version update-check is preserved: the framework-agnostic UpdateChecker stays, with an actix handler/route mounted at GET /version. The axum mock modules and the bunyip-mocks dependency are removed; migrations/ (53 files) are retained.

Test-fixture owner/repo strings ('a8n'/'rus') remain in #[cfg(test)] blocks (inert placeholders, do not ship).

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migrations: deletes the 6 a8n.tools domain seed migrations (seed_applications + the five register_*_oidc_client files) and strips the hardcoded a8n/dmarc oauth_clients INSERTs from create_oidc_clients (the table DDL + index are kept). Bunyip will register its own relying parties (bunyip-web SPA, mokosh-server) via a dedicated seed migration once their client_ids/redirect_uris/secrets are known. 47 generic-schema migrations remain.

Retires the mock: removes crates/bunyip-mocks and seeds/ (the in-memory JSON store the old axum backend used) and drops the workspace member. bunyip-api no longer depends on it. Backend (core + oci + oidc + api) still compiles.

The lone remaining "a8n-tools" string is a comment on the STRIPE_APP_TAG default in a migration; the real default lives in config and is rebranded with the config pass.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix: drop bunyip-mocks workspace member and strip oidc client seeds
Some checks failed
build / Build and push OCI images (pull_request) Has been skipped
build / Lint and type-check (pull_request) Failing after 18s
2b69a97a41
Completes the previous cleanup commit: the prior commit removed crates/bunyip-mocks/ and the domain seed migrations but - due to a git add aborting on an already-removed pathspec - left the workspace member entry and the create_oidc_clients INSERT strip uncommitted, leaving HEAD referencing a deleted member.

This removes "crates/bunyip-mocks" from the workspace members, commits the create_oidc_clients.sql DDL-only change (a8n/dmarc client INSERTs stripped), and updates Cargo.lock. Backend compiles.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(core): rebrand runtime config defaults from a8n to bunyip
Some checks failed
build / Build and push OCI images (pull_request) Has been skipped
build / Lint and type-check (pull_request) Failing after 6s
a35b179957
Swaps the a8n.tools defaults baked into the ported config for bunyip ones: blob/download cache dirs (/var/cache/a8n-* -> /var/cache/bunyip-*), the OIDC lifecycle-event URN (urn:dunite:event:user-lifecycle -> urn:bunyip:event:user-lifecycle), the STRIPE_APP_TAG fallback (a8n-tools -> bunyip), the issuer doc example, and the SMTP_FROM parser test fixture. Cache-dir test assertions updated to match.

Runtime identity stays env-driven (APP_NAME, OIDC_ISSUER, STRIPE_APP_TAG, OIDC_LIFECYCLE_EVENT_KEY); these are only the fallback defaults. Remaining "a8n"/"rus" strings are inert #[cfg(test)] fixtures (fake owner/repo, .a8n.run cookie test) that do not ship.

#BUNYIP-26

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David merged commit 26d07fd543 into main 2026-05-30 19:57:19 +02:00
David deleted branch feat/bunyip-api-on-dunite 2026-05-30 19:57:19 +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!15
No description provided.