manusia
by chozzz
README.md
# manusia
Human-as-a-Service: your AI agent estimates, posts, and polls a task; a vetted
human from a small AU roster answers it from a one-page claim form; you pay
them from an operator-funded AUD pool. **manusia is brand 1** on a
brand-agnostic engine — expanding (Australia → Indonesia, or into a vertical)
is spinning up a brand row, not a fork (doc 22).
Stack: pnpm workspaces + Turborepo + TypeScript strict, Node ≥ 22.
| Layer | Choice |
| --------------- | ---------------------------------------------------------------------- |
| Brain | `apps/api` — Fastify: REST + the 7 tool endpoints + in-process workers |
| Web | `apps/web` — Next.js (claim one-pager lands at `claim.vargos.io`) |
| npx shim | `apps/mcp-stdio` — `@manusiamcp/mcp`: MCP-SDK stdio → brain HTTPS |
| DB | `packages/db` — Prisma + PostgreSQL (homelab `manusia` / `manusia_test`)|
| Auth | `packages/auth` — magic links for humans, credentials for operators, `mk_live_`/`mk_test_` API keys |
| Engine | `packages/engine` — pure domain (state, clocks, fee math, the 7 handlers) |
| LLM | `packages/llm` — local vLLM (estimate + posting gate, degraded-safe) |
| MCP | `packages/mcp` — tool contracts, error mapping (doc 02) |
| Notify / mail | `packages/notifier` (per-brand templates) → `packages/mailer` (Resend) |
| Workers | `packages/workers` — TTL watcher, mock responder (in-process, M1) |
| CLI | `packages/cli` — operator plane (`brands`, `humans`, `categories`, `requests`, `payouts`) |
The spec lives in `docs/` (01–23 + plan); `docs/23-gap-review.md` records the
boilerplate reconciliation (Prisma+Postgres over SQLite, `Tenant`→`Brand`,
magic links, `@haas/*` scope per Vadi's locked decisions).
## 5-minute setup (local)
```bash
cp .env.example .env # fill AUTH_SECRET + homelab DATABASE_URL
docker compose -f infra/docker-compose.yml up -d # throwaway local postgres (or point at homelab)
pnpm install
pnpm db:migrate # apply prisma migrations (base + product schema)
pnpm db:seed # brand 1 (manusia, en/AUD, 15%/A$1.00), categories, operator, API key
cp apps/web/.env.example apps/web/.env.local
pnpm dev # web → :3000, api → :3001
```
Run the mock loop with no roster: `MANUSIA_MOCK=1 pnpm --filter api dev`
(mock `mk_test_` keys / env mock answer requests after `MANUSIA_MOCK_DELAY_MS`).
Try the tools (key from `db:seed` output):
```bash
curl -s localhost:3001/v1/tools/capabilities \
-H "Authorization: Bearer <api key>" -H 'content-type: application/json' -d '{}'
curl -s localhost:3001/v1/tools/request \
-H "Authorization: Bearer <api key>" -H 'content-type: application/json' \
-d '{"task":"STP deadline for our first subbie?","category":"accounting-au","budget":25}'
```
The npx shim (doc 21 §1) — MCP-stdio client → brain HTTPS, no DB in the shim:
```json
{ "mcpServers": { "manusia": { "command": "npx", "args": ["@manusiamcp/mcp"],
"env": { "MANUSIA_API_KEY": "mk_live_…", "MANUSIA_API_URL": "http://127.0.0.1:8443" } } } }
```
Checks CI runs:
```bash
pnpm turbo run lint typecheck test build # all pass without a live DB
```
## The 7 tools (locked, doc 19 B1)
`human.estimate` · `human.capabilities` · `human.request` · `human.status` ·
`human.cancel` · `human.list` · `human.upload` — pure async, offer-based
(`budget` is the price, doc 20), stable error codes in `error.code` (doc 02 §5).
Contracts in `packages/shared/src/tools.ts`; handlers in `packages/engine`.
## CI
- **build** — lint → typecheck → test → build, no live DB.
- **integration** — boots the brain in **mock mode** against a throwaway CI
Postgres (service container, never the homelab), migrates + seeds, then
drives the 7 tools end-to-end: `estimate → request → (mock) in_progress →
answered → status → list`, plus idempotency + error-code assertions
(`apps/api/scripts/integration.ts`, doc 19 DoD #1).
## Production (baseline)
- `infra/systemd/manusia.service` — the brain, `PORT=8443`.
- `infra/cloudflared/config.yml` — `mcp.vargos.io` → brain :8443,
`claim.vargos.io` → web :3000 (doc 19 B4 — no new domain).
- `infra/backup/backup-nightly.sh` — nightly `pg_dump` → TrueNAS (cron),
replaces the spec's `sqlite3 .backup` (doc 23 G8).
## Env
Single source of truth: **root `.env`** (Prisma + API load it; web uses
`apps/web/.env.local`). Full list + defaults in `.env.example`. Homelab DB
credentials are still pending — the placeholders in `.env.example` are
placeholders on purpose.
## Design notes
- **Source-only workspace packages** — `packages/*` export raw TS
(`"exports": "./src/index.ts"`); web transpiles them, the API runs via tsx.
- **The brain is the sole writer** — claim races use
`UPDATE … WHERE status='pending'`; workers are in-process intervals in M1
(the `packages/workers` seam is the extraction point, doc 21 §8).
- **Brand is a row, not a fork** — identity/logo/key prefixes in
`packages/brand` (deployment config); locale/currency/fees/hosts/roster in
the `brands` table (doc 22). Middleware resolves host → path →
`MANUSIA_DEFAULT_BRAND`; everything downstream is brand-scoped.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues