# Agents ## Project Overview `arcanesync` — cloud sync backend for the arcanegram telegram client fork. Authenticates every request via Telegram Mini App `initData` (HMAC-SHA256) and persists per-user sync state in postgres. ## Stack - **Runtime**: Node 24+, tsx (no build step) - **Package manager**: pnpm - **HTTP**: Hono on `@hono/node-server` - **DB**: PostgreSQL via `pg` + drizzle-orm; migrations via drizzle-kit - **Linting**: @antfu/eslint-config v8 (eslint.config.mjs) - **Env**: env-var + native `loadEnvFile` - **Container**: Docker, node:24-alpine; docker-compose for postgres ## Structure ``` src/ ├── index.ts # entrypoint — wires hono app, starts server ├── db/ │ ├── db.ts # pg pool + drizzle instance │ ├── schema.ts # drizzle schema │ ├── migrate.ts # migration runner (pnpm db:migrate) │ └── migrations/ # generated sql ├── lib/ │ ├── initdata.ts # telegram initData hmac validator │ └── initdata.test.ts ├── middleware/ │ └── auth.ts # `Authorization: tma ` guard ├── routes/ │ ├── health.ts # GET /health │ ├── miniapp.ts # GET /miniapp (botfather stub) │ └── sync.ts # GET/PUT /sync └── shared/ └── config.ts # env config ``` ## Endpoints - `GET /health` — liveness - `GET /miniapp` — mini app stub registered with botfather - `GET /sync` — auth required. returns `{version, data}` - `PUT /sync` — auth required. body `{baseVersion, patch}`. 200 on accept, 409 on version conflict (returns current `{version, data}`) Auth header: `Authorization: tma `. Validation enforces hmac correctness and `auth_date` freshness. ## Conventions - ESM only (`"type": "module"`) - Path alias `@/*` → `src/*` — imports must end with `.js` (nodenext resolution) - No build step — tsx runs TS directly in dev and prod - `cross-env` for cross-platform env vars in npm scripts - No useless comments - Split code into multiple files rather than monoliths - Lowercase strings (errors, keys, log messages) where reasonable ## Commands - `pnpm dev` — watch mode - `pnpm start` — production run - `pnpm lint` / `pnpm lint:fix` - `pnpm test` — node:test runner via tsx - `pnpm db:generate` — drizzle-kit generate - `pnpm db:migrate` — apply migrations - `pnpm db:studio` — drizzle studio ## Env | var | default | meaning | |---|---|---| | `PORT` | `3000` | http port | | `DATABASE_URL` | required | postgres dsn | | `BOT_TOKEN` | required | telegram bot token (mini-app-enabled) | | `AUTH_DATE_MAX_AGE_SECONDS` | `86400` | initData freshness window | | `MAX_PAYLOAD_BYTES` | `65536` | per-PUT payload cap | ## Lint Rules - `no-console: off` - `antfu/no-top-level-await: off` - Stylistic: 2-space indent, single quotes - JSON key ordering enforced (auto-fixed) ## Docker Single-stage `node:24-alpine`, no build. Copies source, installs prod deps, runs `pnpm db:migrate` then `pnpm start`. `docker-compose.yml` provides postgres for local dev.