- Node.js 20+
- pnpm 10+ (
corepack enable && corepack prepare pnpm@latest --activate) - Firebase CLI (
npm i -g firebase-tools)
git clone https://github.com/Appsilon/mediforce.git
cd mediforce
pnpm installcp packages/platform-ui/.env.local.example packages/platform-ui/.env.localFill in your Firebase project values. Get them from: Firebase Console > Project Settings > General > Your apps.
| Variable | Description |
|---|---|
NEXT_PUBLIC_FIREBASE_API_KEY |
Firebase API key |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN |
Firebase auth domain |
NEXT_PUBLIC_FIREBASE_PROJECT_ID |
Firebase project ID |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID |
Firebase messaging sender ID |
NEXT_PUBLIC_FIREBASE_APP_ID |
Firebase app ID |
OPENROUTER_API_KEY |
OpenRouter API key (for agent LLM calls) |
PLATFORM_API_KEY |
Platform API key (for server actions) |
packages/
platform-core/ # Shared types, domain models, test factories
platform-ui/ # Next.js UI — the main web application
platform-infra/ # Postgres infrastructure (Drizzle ORM) + Firebase Auth
platform-api/ # API contract schemas + pure handlers (framework-free)
agent-runtime/ # Agent execution engine
workflow-engine/ # Process orchestration engine
example-agent/ # Reference agent implementation
All server data lives in a local Postgres (ADR-0001) — there is no Firestore
data layer. pnpm dev starts the container, runs migrations, and boots the UI
against it. Quick recipes:
# Reset local data (wipes the persistent volume, re-migrates)
docker compose -f docker-compose.yml -f docker-compose.dev.yml down -v && pnpm dev
# Add a migration: edit a schema file under
# packages/platform-infra/src/postgres/schema/
pnpm db:generate # emit NNNN_*.sql + journal entry
pnpm db:migrate # apply locally (pnpm dev auto-runs this)
# Run repo tests against a real Postgres
TEST_DATABASE_URL=postgresql://mediforce:mediforce@localhost:5432/mediforce \
pnpm --filter @mediforce/platform-infra exec vitest run src/postgresDeep dive (inspecting migration state, branch-collision renames, connection pool, troubleshooting): postgres-local-dev.md.
When running with ALLOW_LOCAL_AGENTS=true (via pnpm dev:no-docker), the platform spawns agent CLIs directly as local processes instead of Docker containers. This mode is docker-free but still requires Postgres on :5432 (DATABASE_URL is required at boot) — it does not start the container itself, so run pnpm dev once first or point DATABASE_URL at your own DB. The following tools must be installed and on your PATH:
| Tool | Used by | Install |
|---|---|---|
claude |
ClaudeCodeAgent workflow steps |
Claude Code docs — npm install -g @anthropic-ai/claude-code |
opencode |
OpenCodeAgent workflow steps |
npm install -g opencode-ai |
Verify both are available after installing:
claude --version
opencode --versionWithout ALLOW_LOCAL_AGENTS=true, agents run inside Docker containers instead. In that case you need Docker installed and running, but not the CLIs above.
# Platform UI (default, port 9003)
cd packages/platform-ui && pnpm dev# Unit + integration (vitest)
pnpm test:unit
# Only tests affected by your changes
pnpm test:affected
# With coverage
pnpm test:coverage
# Type checking
pnpm typecheck
# Everything (unit + e2e)
pnpm testHandlers in platform-api are tested against in-memory repositories from @mediforce/platform-core/testing — no mocks, no HTTP, no Firebase emulators, no dev server. The real win over E2E is not raw wall-clock time but zero ceremony: run the file, get the answer. Each handler is a pure function (input, deps) => Promise<output> with per-handler dependency injection, so tests read like the spec: set up repo state, call handler, assert on the return value. The canonical example is packages/platform-api/src/handlers/tasks/__tests__/list-tasks.test.ts, which exercises the listTasks handler backing GET /api/tasks.
E2E tests live in packages/platform-ui/e2e/.
# Terminal 1 — start emulators
pnpm emulators
# Terminal 2 — run tests
pnpm test:e2e # all E2E (L3 + L4)
pnpm test:e2e:api # L3 only — API E2E, no browser (~30s)
pnpm test:e2e:ui # L4 only — UI E2E with real Chromium (~3min)Variants run from packages/platform-ui:
pnpm test:e2e:headed # with browser visible
pnpm test:e2e:ui # interactive Playwright UI modeThe emulator setup automatically:
- Creates a test user (
test@mediforce.dev/test123456) - Seeds Firestore with test data (tasks, process instances, agent runs, audit events)
- Authenticates and saves auth state for all tests
Test structure:
e2e/smoke.spec.ts— unauthenticated testse2e/api/*.journey.ts— L3 API E2Ee2e/ui/*.journey.ts— L4 UI E2Ee2e/helpers/— emulator REST API helpers and seed data
pnpm typecheck— catches type errors (~5s)pnpm test:affected— tests for changed files only (<1s)pnpm test:unit— full L1+L2 (~9s)pnpm test:e2e— if UI/API contract changed (~4min)
pnpm build # builds all packagesStaging and production servers are hosted on Hetzner.
| Environment | SSH access |
|---|---|
| Staging | ssh deploy@204.168.165.57 |
The staging machine also has an sftpuser account with SFTP enabled, used for the Data Landing Zone workflow demo.
All credentials (SSH passwords, etc.) are stored in 1Password under the Mediforce vault.