AWCMS Mini is an EmDash-first, single-tenant governance overlay built on Astro, PostgreSQL, Kysely, and the EmDash CMS integration.
It keeps EmDash as the host architecture and adds Mini-specific governance features for:
- user lifecycle management
- RBAC and ABAC authorization
- protected roles and staff-level rules
- job hierarchy and job assignments
- logical and administrative region governance
- TOTP-based 2FA, recovery, lockouts, and step-up
- audit logs and security events
- governance-aware internal plugin contracts
- EmDash admin extensions for governance operations
This repository is implementation-heavy and now includes:
- the main governance and security schema
- service-layer authorization and rollout helpers
- the
awcms-users-adminadmin extension - plugin governance contract helpers
- operator documentation for recovery and deployment validation
Known current conditions:
- mandatory 2FA rollout configuration exists in the admin/security policy model
- ABAC audit-only rollout flags exist in the authorization service
- some rollout and persistence hardening work is still needed before treating those controls as fully production-complete across multi-instance deployments
- the reviewed live Cloudflare Worker now uses the Hyperdrive-backed PostgreSQL path successfully through the current private-database tunnel posture
- the reviewed Coolify-managed VPS now uses key-only root SSH recovery, and the tunnel token is rotated from root-only VPS-managed secret storage on a monthly timer
- Astro
6.1.8 - React
19.2.5 - EmDash
0.7.0 - PostgreSQL
- Kysely
0.28.16 - Cloudflare adapter via
@astrojs/cloudflare - Node adapter via
@astrojs/nodekept only as an explicit fallback build target during migration
- Install dependencies:
pnpm install- Set the runtime environment values you need for the target deployment.
Required production baseline:
DATABASE_URLMINI_RUNTIME_TARGET=cloudflareSITE_URL- optional
ADMIN_SITE_URLfor a dedicated admin hostname that still points to the same EmDash admin surface MINI_TOTP_ENCRYPTION_KEYTRUSTED_PROXY_MODE=cloudflarefor the supported Cloudflare-hosted path
Optional rollout verification inputs for pnpm healthcheck:
HEALTHCHECK_EXPECT_DATABASE_TRANSPORTHEALTHCHECK_EXPECT_DATABASE_HOSTNAMEHEALTHCHECK_EXPECT_DATABASE_SSLMODEHEALTHCHECK_EXPECT_HYPERDRIVE_BINDING
These values are non-secret assertion inputs. Keep them unset for normal local development unless you intentionally want health checks to fail fast when the runtime points at the wrong reviewed database target.
Recommended public abuse-defense settings:
TURNSTILE_SITE_KEYTURNSTILE_SECRET_KEY- optional
TURNSTILE_EXPECTED_HOSTNAME - optional
TURNSTILE_EXPECTED_HOSTNAMESfor split public/admin hostnames
When configured, Turnstile currently protects the public login, password-reset request, and invite-activation flows.
For split public/admin hostnames, the runtime now derives the expected Turnstile hostnames from SITE_URL and ADMIN_SITE_URL unless you set TURNSTILE_EXPECTED_HOSTNAMES explicitly.
R2 storage baseline when object storage is enabled:
R2_MEDIA_BUCKET_BINDING=MEDIA_BUCKETR2_MEDIA_BUCKET_NAME=awcms-mini-s3R2_MAX_UPLOAD_BYTESR2_ALLOWED_CONTENT_TYPES
Edge API baseline:
/api/v1/healthfor versioned public health checks/api/v1/tokenfor password and refresh-token grants for mobile or external clients/api/v1/sessionfor current-session inspection and self-revocationEDGE_API_ALLOWED_ORIGINSfor any explicit cross-origin browser clientsEDGE_API_MAX_BODY_BYTESfor request-size enforcementEDGE_API_JWT_SECRETfor Bearer-token signing and verification- optional
EDGE_API_JWT_ISSUERandEDGE_API_JWT_AUDIENCE EDGE_API_ACCESS_TOKEN_TTL_SECONDSandEDGE_API_REFRESH_TOKEN_TTL_SECONDS
Current token behavior:
/api/v1/tokensupportspasswordandrefresh_tokengrant types- access tokens are short-lived JWT Bearer tokens signed with
jose - refresh tokens are opaque, hashed at rest in PostgreSQL, and rotated on use
- enrolled 2FA users must satisfy TOTP or recovery-code challenge during the password grant
- protected
/api/v1/*routes accept Bearer tokens and still keep the host identity session as a compatibility fallback
For remote PostgreSQL deployments, DATABASE_URL should target the reviewed SSL hostname id1.ahlikoding.com, prefer sslmode=verify-full when certificate validation is available, and use a non-superuser application role.
If production must temporarily run before hostname validation is fully ready, keep TLS required with an explicitly reviewed interim mode such as sslmode=require and track the follow-on hardening work rather than silently weakening transport defaults.
APP_SECRET should also be set when your host auth/session runtime depends on it. Mini currently falls back to APP_SECRET only if MINI_TOTP_ENCRYPTION_KEY is not set.
- Apply migrations:
pnpm db:migrate- Start the dev server:
pnpm dev- Validate runtime health:
pnpm healthcheckExample direct-path rollout verification:
HEALTHCHECK_EXPECT_DATABASE_TRANSPORT=direct \
HEALTHCHECK_EXPECT_DATABASE_HOSTNAME=id1.ahlikoding.com \
HEALTHCHECK_EXPECT_DATABASE_SSLMODE=verify-full \
pnpm healthcheckThe reviewed browser entry for the EmDash-hosted admin surface is /_emdash/, which redirects into the current EmDash admin surface under /_emdash/admin.
Current single-host baseline:
SITE_URLremains the canonical public hostnamehttps://awcms-mini.ahlikoding.com/_emdash/is the reviewed admin entry URL- the runtime redirects that alias to the current EmDash admin surface under
/_emdash/admin ADMIN_SITE_URL, when configured for compatibility, remains only an optional entry host for the same admin surface
Cloudflare-hosted deployment baseline:
pnpm buildproduces the Worker bundlepnpm deploy:cloudflaredeploys via Wrangler- non-interactive Cloudflare automation should keep
CLOUDFLARE_API_TOKENin.env.local, Wrangler-managed secrets, or CI/CD secret storage rather than tracked files; Tunnel provisioning needsAccount > Cloudflare Tunnel > Edit, DNS provisioning needs zone DNS read/edit permission for the target zone, and Access provisioning needs the relevant Cloudflare Access/Zero Trust scopes - local operator wrappers should load
.env.localand.envas environment data, not by sourcing them as shell code wrangler.jsoncdefines the Worker, assets, observability, the reviewed public custom domain forawcms-mini.ahlikoding.com, theMEDIA_BUCKETR2 binding forawcms-mini-s3, and the active reviewed Hyperdrive binding used by the live Cloudflare deploymentwrangler.jsoncalso declares the reviewed required Worker secret names, and the shared local Astro wrapper now fails fast when those required secrets are missing from env-managed local files or process envDATABASE_TRANSPORT=hyperdriveis the current reviewed production posture; local build and typecheck wrappers deriveCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVEfromDATABASE_URLso credentials stay env-managed- the Mini auth middleware keeps the EmDash setup shell database-lazy so
/_emdash/admin/setupcan render during database transport reconciliation instead of failing early with a Worker exception - the reviewed PostgreSQL tunnel connector now reads its runtime token from root-only VPS-managed storage and rotates that token monthly on the VPS
- Astro's Cloudflare adapter uses the default
SESSIONKV binding for sessions unless you override it explicitly - for Coolify-managed resources on the VPS, the reviewed secret surface is Coolify Environment Variables with locked secrets, runtime/build scoping, and Docker Build Secrets for reviewed build-time sensitive inputs
pnpm smoke:cloudflare-admin
pnpm verify:live-runtime
pnpm check:secret-hygiene
pnpm check
pnpm lint
pnpm format
pnpm typecheck
pnpm test:unit
pnpm build
pnpm healthcheck
pnpm db:migrate
pnpm db:migrate:status
pnpm db:migrate:down
pnpm db:migrate:emdash:status
pnpm db:migrate:emdash:repair
pnpm db:seed:administrative-regionsValidation baseline:
pnpm check:secret-hygieneis the focused regression check for maintained scripts, config examples, and operator docs that must not gain hardcoded credentials or inline tokens.pnpm verify:live-runtimeis the focused combined verification path for the current reviewed Cloudflare-hosted Hyperdrive baseline, reusingpnpm healthcheck,pnpm db:migrate:emdash:verify, andpnpm smoke:cloudflare-admin, and it assertsHEALTHCHECK_EXPECT_DATABASE_TRANSPORT=hyperdriveplusHEALTHCHECK_EXPECT_HYPERDRIVE_BINDING=HYPERDRIVEby default unless explicitly overridden.pnpm smoke:cloudflare-adminis the focused live-target smoke check for the reviewed/_emdash/admin alias and/_emdash/admin/setupshell path.- Use
pnpm checkas the default local pre-change validation path. pnpm lintandpnpm formatcurrently cover the maintained docs/config surface with Prettier.- Keep issue-specific validation commands in addition to the baseline when a task requires them.
src/
auth/ Mini auth handlers, middleware, and step-up flows
config/ runtime config parsing
db/ Kysely client, migrations, repositories, transactions
integrations/ Astro + EmDash integration wiring
plugins/ admin extension and plugin governance helpers
security/ policy and runtime security helpers
services/ governance, audit, security, and authorization services
tests/unit/ unit coverage for services, plugin helpers, and admin flows
docs/ architecture, governance, security, plugin, admin, and process docs
skills/ local repository skills for recurring AI-assisted tasks
Use this order when reading or updating repository guidance:
REQUIREMENTS.mdAGENTS.mdREADME.mdDOCS_INDEX.md- focused docs under
docs/**
REQUIREMENTS.md- repository requirements baselineAGENTS.md- agent execution rules and repo-specific guidanceDOCS_INDEX.md- documentation mapdocs/README.md- docs folder entrypointdocs/architecture/overview.md- system summarydocs/process/migration-deployment-checklist.md- release checklistdocs/security/emergency-recovery-runbook.md- recovery guidance
- The repository follows an issue-driven workflow documented in
docs/process/github-issue-workflow.md. - The current codebase is intentionally EmDash-first and must not drift toward a second standalone CMS core.
See LICENSE.md.