# Fiscal Clone 3.0 Turbopack-first rebuild of a fiscal.ai-style terminal with OpenClaw integration. ## Stack - Next.js 16 App Router - Bun runtime/tooling - Elysia route layer mounted in Next Route Handlers - Turbopack for `dev` and `build` - Better Auth (email/password + magic link) - Drizzle ORM (SQLite) + Better Auth Drizzle adapter - Internal API routes via Elysia app module (`lib/server/api/app.ts`) - Eden Treaty for type-safe frontend API calls - Workflow DevKit Local World for background task execution - SQLite-backed domain storage (watchlist, holdings, filings, tasks, insights) - OpenClaw/ZeroClaw analysis via OpenAI-compatible chat endpoint ## Run locally ```bash bun install bun run db:generate bun run db:migrate bun run dev ``` Open [http://localhost:3000](http://localhost:3000). The default database path is `data/fiscal.sqlite` via `DATABASE_URL=file:data/fiscal.sqlite`. ## Production build ```bash bun run db:migrate bun run build bun run start ``` ## Docker deployment ```bash cp .env.example .env docker compose up --build -d ``` For local Docker, host port mapping comes from `docker-compose.override.yml` (default `http://localhost:3000` via `APP_PORT`, and `http://localhost:4000` for OpenClaw via `OPENCLAW_PORT`). OpenClaw is included as a Compose service (`openclaw`) and is built by default from `OPENCLAW_BUILD_CONTEXT` (set to `Francy51/coolify_ZeroClaw` in `.env.example`). If that Gitea repo is private, set `OPENCLAW_BUILD_CONTEXT` with embedded credentials (`https://:@.../coolify_ZeroClaw.git`) or point `OPENCLAW_IMAGE` to a prebuilt image you can pull. The app container defaults to `OPENCLAW_BASE_URL=http://openclaw:4000` unless you explicitly set a different `OPENCLAW_BASE_URL`. On container startup, the app applies Drizzle migrations automatically before launching Next.js. The app stores SQLite data in Docker volume `fiscal_sqlite_data` (mounted to `/app/data`) and workflow local data in `fiscal_workflow_data` (mounted to `/app/.workflow-data`). Workflow Local World uses filesystem state plus an in-memory queue. On container restart, queued in-flight jobs may be lost. Docker images use Bun (`oven/bun:1.3.5-alpine`) for build and runtime. ## Coolify deployment This compose setup is compatible with Coolify as-is (it uses named Docker volumes, not host bind mounts). Required environment variables in Coolify: - `DATABASE_URL=file:/app/data/fiscal.sqlite` - `BETTER_AUTH_SECRET=` - `BETTER_AUTH_BASE_URL=https://fiscal.b11studio.xyz` - `BETTER_AUTH_TRUSTED_ORIGINS=https://fiscal.b11studio.xyz` - `WORKFLOW_TARGET_WORLD=local` - Optional: `WORKFLOW_LOCAL_DATA_DIR=/app/.workflow-data` Operational constraints for Coolify: - Keep this service to a single instance/replica. SQLite is file-based and not appropriate for multi-replica shared-write deployments. - Ensure the two named volumes are persisted (`fiscal_sqlite_data`, `fiscal_workflow_data`). - Workflow Local queueing is in-memory; in-flight queued jobs may be lost on restarts. - Docker build forces `WORKFLOW_TARGET_WORLD=local` to avoid stale Coolify build args referencing `@workflow/world-postgres`. - Runtime Compose config also pins `WORKFLOW_TARGET_WORLD=local` for the same reason. ## Environment Use root `.env` or root `.env.local`: ```env # leave blank for same-origin API NEXT_PUBLIC_API_URL= DATABASE_URL=file:data/fiscal.sqlite BETTER_AUTH_SECRET=replace-with-a-long-random-secret BETTER_AUTH_BASE_URL=https://fiscal.b11studio.xyz BETTER_AUTH_TRUSTED_ORIGINS=https://fiscal.b11studio.xyz OPENCLAW_BASE_URL= OPENCLAW_API_KEY= OPENCLAW_MODEL=zeroclaw OPENCLAW_AUTH_MODE=none OPENCLAW_PORT=4000 OPENCLAW_IMAGE=coolify-zeroclaw:local OPENCLAW_BUILD_CONTEXT=https://gitea-hs848cs8kgs840o8c8s8cwkk.b11studio.xyz/Francy51/coolify_ZeroClaw.git OPENCLAW_DOCKERFILE=Dockerfile # for OPENCLAW_AUTH_MODE=basic # OPENCLAW_BASIC_AUTH_USERNAME=your_nginx_user # OPENCLAW_BASIC_AUTH_PASSWORD=your_nginx_password # optional: forward API key in a custom header when using basic/none auth # OPENCLAW_API_KEY_HEADER=X-OpenClaw-API-Key SEC_USER_AGENT=Fiscal Clone WORKFLOW_TARGET_WORLD=local WORKFLOW_LOCAL_DATA_DIR=.workflow-data WORKFLOW_LOCAL_QUEUE_CONCURRENCY=100 ``` If OpenClaw is unset or invalidly configured, the app uses local fallback analysis so task workflows still run. For OpenClaw behind Nginx Basic Auth, use: ```env OPENCLAW_BASE_URL=https://your-nginx-host OPENCLAW_AUTH_MODE=basic OPENCLAW_BASIC_AUTH_USERNAME=your_nginx_user OPENCLAW_BASIC_AUTH_PASSWORD=your_nginx_password # optional if upstream still needs an API key in a non-Authorization header OPENCLAW_API_KEY=your_key OPENCLAW_API_KEY_HEADER=X-OpenClaw-API-Key ``` ## API surface All endpoints below are defined in Elysia at `lib/server/api/app.ts` and exposed via `app/api/[[...slugs]]/route.ts`. - `ALL /api/auth/*` (Better Auth handler) - `GET /api/health` - `GET /api/me` - `GET|POST /api/watchlist` - `DELETE /api/watchlist/:id` - `GET|POST /api/portfolio/holdings` - `PATCH|DELETE /api/portfolio/holdings/:id` - `GET /api/portfolio/summary` - `POST /api/portfolio/refresh-prices` - `POST /api/portfolio/insights/generate` - `GET /api/portfolio/insights/latest` - `GET /api/filings` - `POST /api/filings/sync` - `POST /api/filings/:accessionNumber/analyze` - `GET /api/tasks` - `GET /api/tasks/:taskId`