AstroBaaS

Where it is going

Maturity phases

Generated from ROADMAP.md in the AstroBaaS repository. The repository is the source of truth; this page is a copy of it.

This document is a pragmatic, phased plan to take the repo from “prototype” to “alpha-usable”. Each phase is small enough to ship in one PR and leaves the app in a working state. Earlier phases are blockers for later ones.

Progress against this plan is recorded in CHANGELOG.md. The extensibility surface for theme/plugin authors — and what’s deferred past alpha — is documented in PLATFORM.md and STABILITY.md.


Guiding principles

  1. Server-only secrets. Anything sensitive (sessions, password hashes, upload writes) must run server-side. The admin UI is JS-light; rely on regular HTML form posts where possible.
  2. One source of truth. Today: LowDB (db.json). The Supabase code is a half-finished alternative path — we cut it (Phase 1) rather than maintain both.
  3. Boring tech first. No JWTs, no Redis, no message bus. A signed cookie + in-memory rate-limit is enough for alpha.
  4. Working > complete. A feature flag is preferable to a half-broken page.

Phase 0 — Repo hygiene (≈ 30 min)

Goal: a clean baseline.

  • Remove dist/ from the working tree (it’s in .gitignore already).
  • Remove db.json from version control. Ship db.seed.json as a template and copy on first run.
  • Remove the unused Supabase client/types/migrations (src/lib/supabase.ts, src/lib/database.types.ts, supabase/).
  • Delete src/pages/admin/posts/test.astro (debug scratch).
  • Add npm run typecheck script.

Done when: git status is clean after npm run build.


Phase 1 — Authentication & security foundations (this PR)

Goal: nobody can hit admin/API without logging in.

  1. Session store: signed-cookie sessions (HMAC-SHA256 with AUTH_SECRET). No third-party deps; small helper in src/lib/auth.ts.
  2. Password hashing: PBKDF2 from node:crypto (no native bcrypt build). Add password_hash and password_salt columns to the User type and a seeded admin (admin@local / admin) on first boot.
  3. /login, /logout Astro pages + POST /api/auth/login, /api/auth/logout.
  4. Middleware (src/middleware.ts):
    • Reads the session cookie, attaches locals.user.
    • Blocks unauthenticated requests to /admin/* and write methods on /api/* (everything except whitelisted public GETs).
    • Adds security headers: X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, a starter CSP, and Permissions-Policy.
    • Adds a per-IP in-memory rate-limit on /api/* (60 req / min).
    • Issues a CSRF token cookie + verifies it on non-GET API calls.
  5. Wire the AdminHeader sign-out button to POST /api/auth/logout.

Done when: logged-out user hitting /admin is redirected to /login; API write calls without a valid session return 401.


Phase 2 — API hardening (next)

  1. Standardize on ApiResponseBuilder everywhere (kill ad-hoc shapes).
  2. Introduce a tiny validator (src/lib/validate.ts) — no zod dep needed for alpha; a typed schema helper is plenty.
  3. Convert non-RESTful routes:
    • POST /api/posts/createPOST /api/posts
    • PUT /api/posts/updatePUT /api/posts/[id]
    • DELETE /api/posts/deleteDELETE /api/posts/[id]
    • same for categories, users, media. Keep thin re-export shims at the old paths for one release.
  4. Fix content/changes: align positional args, use ISO string since.
  5. Sanitize HTML on input (DOMPurify equivalent on the server — small allow-list sanitizer is fine for alpha).

Done when: every endpoint returns { success, data | error }, requires auth for writes, and rejects malformed bodies with 422.


Phase 3 — Content model maturity

  1. Pages as a first-class type (alongside posts) so /about, /contact come out of the DB instead of being hard-coded .astro files.
  2. Slug auto-generation + uniqueness check at create time.
  3. Status workflow: draft → review → scheduled → published → trashed.
  4. Tags as their own collection (currently a string[] on Post).
  5. Post revisions: append-only post_revisions table, capped at N per post.
  6. Search endpoint: GET /api/search?q=… over title + content (LIKE for now).

Phase 4 — Media & uploads (make uploads real)

  1. Persist files to public/uploads/yyyy/mm/<hash>.<ext> on POST.
  2. MIME-type allow-list + size limit (10 MB default).
  3. Compute width/height for images, store on the MediaFile record.
  4. DELETE /api/media/[id] removes the row and the file.
  5. Use the AlertText helper to display per-file upload errors.

Phase 5 — Public site polish

  1. /sitemap.xml (SSR endpoint reading posts + pages).
  2. /rss.xml for posts.
  3. Real OG image fallback (site setting og_image).
  4. Inject theme CSS variables in BaseLayout server-side (drop the fetch-on-load roundtrip that flashes default colors).
  5. 404 page (src/pages/404.astro).
  6. Robots.txt.

Phase 6 — Admin UX

  1. Real dashboard stats (count posts/users/media; recent activity from contentChanges).
  2. Pagination + search on /admin/posts.
  3. Bulk actions: delete, publish, change category.
  4. Working user-menu profile + preferences pages.
  5. Toast notifications on every mutating action.

Phase 7 — Observability & DX

  1. Structured request log (one line per request, JSON).
  2. Error boundary page + a single reportError() shim.
  3. npm test running a smoke test that boots the dev server and hits the public + admin routes.
  4. CI workflow (.github/workflows/ci.yml) running typecheck, build, and the smoke test.

Phase 8 — BaaS / headless (landed)

Phases 0–7 delivered the secure CMS. Phase 8 turned it into a backend a separate frontend or an AI agent can use. All of the following are shipped and tested (see VISION.md for the strategy and CHANGELOG.md for detail):

  • A — Cross-origin access: API-key/bearer auth + configurable CORS; agent-readable contract (/llms.txt, /openapi.json).
  • B — Durable persistence: pluggable Storage with a libSQL/SQLite driver (DATABASE_URL); lowdb stays the local default. See STORAGE.md.
  • C — Effortless wiring: typed SDK astrobaas/client, the astrobaas CLI (init/secret/setup), and the astrobaas-mcp MCP server.
  • D — Events: signed outbound webhooks on content lifecycle events.

See INTEGRATION.md for the usage guide.

Open follow-ups (post-alpha)

The full, themed, prioritized list lives in BACKLOG.md. The headline items:

  • Packaging for external use — the public exports point at TS source; ship built .js/.d.ts + publish to npm so non-Vite consumers can install it.
  • Relational storage driver ✅ shipped (DATABASE_DRIVER=relational, per-entity rows). Remaining: a fully-normalized column schema + an optional Postgres adapter.
  • Webhook delivery durability — retry/backoff + a delivery log (today: fire-and-forget, 5s timeout, no retry).
  • Realtime subscriptions — push (SSE/WebSocket) instead of webhook polling.
  • Shared rate-limiter for multi-replica deploys (today: in-process).
  • Hosted option / one-click templates for the no-VPS audience.

Out of scope for alpha

  • Multi-site, i18n, e-commerce, GraphQL, WebSocket live preview.
  • A formal plugin marketplace.
  • A separate background-job worker. Scheduled posts can be a request-time check in alpha.

These are valuable but each is bigger than the rest of alpha combined.