Ray Davis Portfolio

Full-stack software engineering — front-end product, back-end APIs, real-time & AI integrations, application security, CI/CD, and AI-orchestrated delivery on self-hosted infrastructure.

🔗 Live demo → demo.ardeeweb.com

A writeup of work I designed and shipped for a production multi-tenant SaaS platform (Next.js App Router on Firebase / Google Cloud, serving multiple isolated business tenants from one codebase). I work across the stack: the front-end product UI customers and operators use every day, the back-end APIs and data model behind it, the security controls that keep tenants isolated, and the delivery pipeline that ships it all. Specific project internals are kept generic, but the mechanisms and patterns are real — and the live platform is linked above.

Contents


At a glance

Frontend

Backend and APIs

Security

CI/CD and delivery


How the guards actually work

The security model rests on a few load-bearing ideas — each enforced in one place, and drift-proofed by CI so it can’t quietly rot as the codebase grows.

One authorization chokepoint every route flows through

Every API handler is wrapped by a single withRoute function that, before the handler body runs, resolves the tenant server-side (from the verified session or route — never the request body), checks a typed access level (public / user / member / permission / owner / superadmin / machine-cron / machine-webhook), enforces a cross-tenant scope guard (an admin of tenant A can’t act on tenant B by changing a path param), applies any per-permission re-auth / approval gate, and logs a security event. The same wrapper carries the feature-gate and the cross-scope (workspace) guard. So access, tenancy, sub-scope, and entitlement are all decided in one audited seam — not re-implemented per handler, where one of them is eventually forgotten.

Every enforced invariant has a CI coverage guard

The pattern that ties it together: whenever the rule is “every X must do Y,” a build-failing guard catches a new X that isn’t covered. A family of check:* guards enforces that every route declares access, every route under a feature prefix declares its gate, every ticket mutation writes to the audit ledger, every AI call routes through the scrubber, every platform page self-gates its own auth (a page layout is not an auth boundary), and every cross-tenant isolation surface is registered in a ledger. The point isn’t any single check — it’s that “someone will remember to do the secure thing” is replaced by “the build fails if they don’t.”

Isolation and redaction are server-side, allow-list by construction

Cross-tenant (and cross-scope) isolation lives in the query and the guard, not the UI — sensitive fields are deleted from the response, not hidden in the page. A forbidden foreign record returns 404, not 403, so status codes can’t become a cross-tenant existence oracle. And every customer-, scoped-, or adopter-facing projection is built from an explicit allow-list of safe fields — never a block-list — so a newly-added internal field can’t leak by omission. The critical projections and guards sit on a mutation-testing list that verifies the tests actually catch a deliberate break.

All AI egress is governed at one chokepoint

Every LLM call routes through a single governed seam: a mandatory scrubber, a hard never-send list (card numbers / private keys block the call), content-free logging, and a CI guard that fails the build if a new AI feature reaches a model without going through it. A feature inherits the governance for free — and structurally cannot skip it.


AI-orchestrated development and a self-hosted delivery fleet

How the work gets built

A large share of the recent work was delivered by architecting and driving AI coding agents — decomposing a feature into a grounded plan, dispatching parallel agents to build slices in isolated worktrees, and running each change through the full gate (type-check, tests, the security guards, a focused security review) before it merges. The discipline is human-in-the-loop and never-auto-merge: agents propose, a human reviews and approves, and production deploys stay a deliberate, separate step. It’s the same “suggest, don’t act” rule the product’s own AI features follow — applied to the way the product itself is built.

A self-hosted fleet, one role per box

To support that workflow — and to keep a private repo’s CI affordable — I’m standing up a small fleet of self-hosted machines, each with a single deliberate role, physically separated so that resource contention, failures, and trust boundaries never bleed across:

Role What it does Status
Always-on development host Runs the AI coding agents continuously in detachable sessions; syncs plans + working memory ✅ Built, in use
Observability + fixtures hub Self-hosted error tracking, a report host, and a persistent, seeded test database every CI run and integration test reproduces from ✅ Built, in use
CI runner A clean, ephemeral environment per job that finally makes mutation testing affordable to run continuously (it’s too expensive on metered cloud minutes) — plus the full suite, build, and security scans 🔜 Planned
Isolated security sandbox A network-isolated, no-egress box for adversarial / pen-test tooling, wiped between runs 🔜 Planned
Heavy-compute + AI node Off the merge path: nightly exhaustive test / mutation / DAST runs, and on-prem LLM inference on a GPU so privacy-sensitive AI work stays on owned hardware 🔜 Planned

The governing principle is centralize shared services, isolate contended or untrusted compute: the hub seeds and observes everyone; the compute boxes are specialists that never interfere. Only two of them ever face production — error ingestion and AI inference — reached over an authenticated tunnel with a fail-closed circuit-breaker, so a home machine can never take production down and can never leak a privacy-mandated AI task to the cloud. (Production connectivity + failover: planned.)


Technical appendix

Frontend architecture & component system

Theming & customization

Feature UIs (stateful client work)

Backend, APIs & integrations

Real-time voice agent (Twilio Media Streams ⇄ Gemini Live)

AI-assisted ticketing & intents

AI data governance

Authentication & sessions

Authorization (deny-by-default + RBAC)

Feature gating & entitlements

Payments (provider abstraction)

Multi-tenant isolation

Secrets management

Input validation & API hardening

Data-layer hardening (server/client boundary)

HTTP security headers / CSP

Security testing (four layers)

  1. Firestore/Storage Rules tests (emulator) — tenant isolation and default-deny.
  2. Authorization-matrix tests — every route’s access kind + permission.
  3. Input-validation tests — schema rejection on write paths.
  4. Browser E2E (Playwright) — auth redirect + real security headers over HTTP. Plus a scheduled live auth canary against the deployed site.

Monitoring, logging & alerting

Supply-chain & repository security

CI pipeline (jobs)

Lint · Type check · Unit tests (coverage floors enforced) · Route access gate · Next.js build · Cloud Functions build · Firestore rules tests · E2E smoke (Playwright) · Secret scan (gitleaks) — parallelized, cancel-in-progress on superseded runs, heavy jobs gated to PRs, build artifact reused by E2E. All required by branch protection on the main branch.

Deployment & environments

Representative incident-style fixes