Aumik internal handbook
The team's single reference for how Aumik Labs builds: our stack, how to get set up, how we ship, the platform we run on, and the tooling. Pick a card or use the sidebar.
The platform in one diagram
+------------------- aumik-agents (Bun · Mastra) --------------------+
chat | agents + tools · BOOKING service (central) · comms-manager |
widget → | REST: /api/scheduling/{resource}/... · /api/agents/{id}/stream |
(widgets) +----------------+--------------------------------------+------------+
| one booking API for every product | email/SMS/WhatsApp
aumik-ui (web, Tamagui) --+ ▼
apps/* + @aumik/portal --+ Resend · Meta WhatsApp · SMS
aumik-native → aumik-player (DOOH TV player) (orchestrated by Novu)
|
+--------------------+-- shared box (Hetzner · Uncloud) ------------------+
| Postgres 16 + pgvector · Redis 7 · Infisical · Caddy · apps |
+-----------------------------------------------------------------------+How we're organized
Aumik runs in four role tracks. Most people live mainly in one:
| Track | What they do |
|---|---|
| Dev | Scaffold, build, deploy, and migrate Aumik apps. |
| Content | Books, marketing video, and song videos (Tirhut Press, Balmukund). |
| PO | Portfolio + team rituals — standups, milestones, access. |
| Ops | Deploy-time plumbing — secrets, infra. |
Dev onboarding
Getting a new dev or intern productive: access, the lay of the land, and how work actually moves.
1. Get access
- Send your GitHub handle to your PO — you'll be added to the
aumik-labsorg and granted collaborator access on the repos you're assigned to. - You'll be invited to Slack —
#aumik-devis the main dev channel,#standupis where the async daily standup runs. - Clone the repo(s) you own. Always check the brand index for the canonical remote before pushing (a few repos were renamed and some local clones still point at old slugs).
2. Learn the lay of the land
Read the Monorepos page first — most work happens in three runtime-split monorepos, not one-repo-per-app. Then skim the brand index for what exists. Each app/brand may carry its own CLAUDE.md with stack specifics — read it before working in that repo.
3. Roles & how work flows
- Apijay sets direction at the milestone level.
- Aniket (
aniket-j4332, lead dev) breaks milestones into per-repo dev issues, assigns devs, and runs PR review → sign-off → release to prod. - Devs own the implementation on their assigned repos (available evenings IST + flexible).
The flow:
- Milestones / tracking stories live in
aumik-infra, assigned to Aniket. - Aniket creates dev issues kept closest to their repo (per-app repo,
aumik-ui,aumik-native,aumik-agents,aumik-infra), assigns the owner, and tracks them on the board. - The cross-cutting tracking story is the one exception to "closest to the repo" — it lives where the program does.
4. The board
Everything is tracked on Aumik Dev — org Project #9 (github.com/orgs/aumik-labs/projects/9). Columns: Todo → In Progress → In Review → Done. Every dev issue goes here; per-person boards are superseded.
workflow_call, aumik-infra#11.)5. How we sync
Two channels of record: GitHub issues are the work log; Slack is for "look now" — and they point at each other. The full conventions (issue comments, PR keywords, which channel for what, async standup) are in Working model.
6. The build philosophy
- Clickable HTML prototypes first. A single self-contained clickable HTML mock (hosted at
proto.aumik.co) is the pre-dev design artifact devs build from. - Know what you're building. Shared building blocks (notifications, booking, auth, the portal) are platform foundations — invest and do them right, don't rush. One-off, single-brand features still get simple first cuts.
- Isolate concurrent work with git worktrees rather than switching branches in a shared checkout.
Working model — issues & Slack
How we plan, track, and communicate. Two channels of record: GitHub issues for the work, Slack for "look now." Keep them pointing at each other.
GitHub issues & comments are the source of truth
- Work lives in issues, not chat. Every substantive, multi-step piece of work has a tracking issue. If one doesn't exist, open it and use it as the log.
- Post progress as issue comments at meaningful milestones — what you're doing, what's done, what's blocked, with links to PRs/commits. Comment at inflection points, not every step.
- Issues live closest to the repo doing the work (the app repo,
aumik-ui,aumik-native,aumik-agents,aumik-infra). The one exception is the cross-cutting tracking story, which lives where the program does (usuallyaumik-infra) and is assigned to Aniket. - Everything is on the board: Aumik Dev — org Project #9 (Todo → In Progress → In Review → Done).
Linking PRs to issues
- Close the issue from its PR with a keyword in the PR description:
Closes #12(same repo) orCloses aumik-labs/<repo>#12(cross-repo). Closes/Fixes/Resolvesauto-close on merge; a bare#12only cross-references.- Milestone naming:
Week of YYYY-MM-DD(Mon→Sun); Friday-EOD retro paragraph on the milestone.
Slack — read & post
Slack is for "look now," and it always points back to the issue. The channels you'll use:
| Channel | Use it for |
|---|---|
#dev | Day-to-day dev chatter, questions, quick coordination. |
#aumik-dev | Urgent + cross-cutting: prod incidents, "I'm blocking a teammate," changes others must act on before they deploy. @-mention the owner; link the issue. |
#standup | Async daily standup (Mon–Fri): reply in-thread to the morning prompt with Did / Doing / Blocked. A digest is collated in the evening. |
#engineering · #all-aumik-hands | Broader engineering and company-wide. |
Posting conventions
- Urgent only in
#aumik-dev. "Urgent" = blocking someone, a prod incident, or a deploy-affecting change. Routine progress stays on the issue. - @-mention the owner and link the issue/PR — the Slack note is a pointer; the issue is the record.
- Use threads. Reply in-thread to keep channels readable; broadcast to the channel only when it needs everyone's eyes.
- Never paste secrets into Slack or issues (see Secrets).
Reading / finding information
- Channels are your sources — search Slack for prior context, but treat the linked issue/PR as authoritative (chat goes stale).
- The evening digest in
#standupis the daily snapshot of who's doing what + current blockers.
aumik-po-daily-standup posts the standup prompt and collates the evening digest, and aumik-po-standup-triage reads that digest and routes action items. See Using aumik-skills. (Claude can also read/post Slack directly via the Slack connector once it's enabled in your setup.)Tech stack
What we build with, and — more importantly — which stack to reach for given the kind of app.
The app stack
The default full-app stack is Tamagui Takeout v2:
| Layer | Tool |
|---|---|
| App framework | One (Vite-based RN + web, replaces Next.js + Expo Router) |
| UI | Tamagui |
| Realtime sync | Zero (Rocicorp) |
| Auth | Better Auth |
| ORM | Drizzle |
| Package manager | Bun (not pnpm/npm) |
Other stacks you'll see:
- Agents (
aumik-agents): Bun · Mastra · Drizzle — the HTTP agent backend. - Cross-platform / TV (
aumik-native): ReNative — uses yarn, not bun (ReNative breaks on bunworkspace:*). - Lovable exports (
aumik-astro,aumik-dev,aumik-food,aumik-cleaning): Vite · React · Shadcn · Supabase — being converted to Takeout v2, with the original Lovable export kept as a read-only reference.
Which stack / where to host — the tier model
Hosting follows the tier of the app, not a one-size template. Each app declares its tier.
Tier A — marketing / SSG-only
No backend. Cloudflare Pages (~$0–5/mo). Promote to Tier B the moment it needs an API or auth.
Tier B — full Takeout apps (web + mobile + auth + realtime) — the default
Uncloud on Hetzner. One shared VPS hosts multiple Aumik apps as Docker services behind Caddy (~$5–20/mo per VPS regardless of app count, until traffic forces a split). Mobile via EAS Build; OTA via Hot Updater. This is the portfolio default for full apps.
Tier C — escape hatch
SST / AWS. Reserved for any single app that demonstrably needs Aurora auto-scaling, AWS-native compliance, or an enterprise contract requiring AWS. Not the default — currently zero apps live here.
Monorepos — what goes where
Three runtime-split monorepos. Pick by runtime, not by product.
| Monorepo | Runtime | Holds | Toolchain |
|---|---|---|---|
aumik-ui | Web (Tamagui/One) | All web UI apps + shared @aumik/* packages (widgets, portal, tsconfig, eslint-config) | bun · Tamagui · Turbo |
aumik-native | ReNative (TV/mobile) | Cross-platform/TV apps — first app aumik-player | yarn · Metro · Turbo |
aumik-agents | Bun · Mastra | Shared AI backend: agents, tools, the booking service, comms-manager | bun · Mastra · Drizzle |
Web apps gather into aumik-ui one PR per app (pattern proven on hospitality, aumik-ui#1): app under apps/<name>/, the Takeout framework hoisted to shared packages/, app overrides moved to the root package.json, seed bun.lock from the app's lockfile.
App inventory gathering into aumik-ui: hospitality (pilot, done) · assistants · alumni · food · dev · health · cleaning · astro.
aumik-dooh spans two monorepos
aumik-dooh is the DOOH (digital signage) product, split by runtime:
aumik-player→aumik-native— the on-screen player: installs on ReNative TV platforms (Tizen/webOS/Android TV/Fire TV); activate a screen → it plays scheduled videos/images. (Not a CMS.)dooh→aumik-ui— the web management portal, two-sided: SSP (screen owners: activate/manage screens, playlists, scheduling, layouts, widgets) + DSP (advertisers: buy inventory, run campaigns). Mocked at the dooh prototype.dooh-landing— marketing site. Not anaumik-uiapp — it's a clickable lo-fi prototype inaumik-prototypes(per the ADLC prototype convention); rebuilt as a real app only if/when productized.
@aumik/portal — the shared self-service shell
Every Aumik product gets a customer self-service portal. Rather than per-product rebuilds, there's a generalized @aumik/portal package in aumik-ui: Better Auth customer accounts (phone-OTP) and self-service surfaces (starting with My Bookings). Each product mounts it scoped to its {resource}. Mocked in the scheduling prototype.
CI/CD
Per-repo hand-rolled CI is deferred — it lands as the reusable Aumik Quality Gate workflow_call (aumik-infra#11) + self-hosted Argos CI (aumik-infra#6), consumed by each monorepo. See Dev onboarding.
Running an app locally
How to boot any aumik-ui app (Takeout / One + Zero) on your machine — the two run modes, the ports, and the errors that actually stop you.
Prerequisites
- Node 24.3.0 — pinned in each app's
.nvmrc. Runnvm usefirst. Node 25 gives 500s on SSR — this is the #1 "it won't start" cause. - bun (not pnpm) —
aumik-uiispackageManager: bun@1.3.x. Install deps once withbun installfrom the repo root. - Docker — only for the full run mode (see below). Not needed for
bun lite.
The local stack per app
A Takeout app is more than a web server. Booting it fully means four pieces:
| Piece | What it does | Default port |
|---|---|---|
| web (One/Vite) | The app itself — SSR + client | 8081 |
| Postgres | Source-of-truth DB + auth | 5433 |
| Zero (zero-cache) | Sync engine — every list/query in the UI streams over its WebSocket | 4848 |
| MinIO | S3-compatible media storage | 9200 |
WebSocket … refused. An empty screen is almost always a backend problem, not a UI bug.Two run modes
1. Lite (no Docker) — recommended for UI work. One embedded process (orez) provides Postgres + Zero + MinIO, auto-migrates, and starts the web server:
cd ~/Dev/aumik-repo/aumik-ui
nvm use # → 24.3.0
cd apps/<app>
bun lite
2. Full (Docker) — when you need the real containerized stack. Backend in one terminal, web in another:
# terminal 1 — brings up pgdb, migrates, then full compose (pgdb + zero + minio)
bun backend
# terminal 2 — web/One server
bun dev
Or bring up just what you need (e.g. the sync engine) with compose directly — but pass both env files so the per-app port offsets apply (see below):
docker compose --env-file .env --env-file .env.development up -d pgdb zero
bun db:migrate # ensure the app's zero publication exists
Ports & PORT_OFFSET
So multiple apps run side by side, each app offsets the defaults. dooh, for example, uses PORT_OFFSET = +5 → web 8086, Postgres 5438, Zero 4853, MinIO 9205. The values live in apps/<app>/.env.development (VITE_PORT_POSTGRES, VITE_PORT_ZERO, …). Always drive Docker through the bun scripts (or pass --env-file .env --env-file .env.development) — a bare docker compose up only reads .env and falls back to the defaults, colliding with whatever other app holds 5433/4848/9200.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 500s on SSR / white page on boot | Wrong Node version | nvm use (must be 24.3.0, not 25) |
connect ECONNREFUSED 127.0.0.1:54xx in the server log; sign-in fails | Postgres/backend not running | Start Docker + bun backend, or use bun lite |
Lists empty; console loops WebSocket … :48xx … ERR_CONNECTION_REFUSED | Zero (zero-cache) not running | docker compose --env-file .env --env-file .env.development up -d zero |
Zero container crash-loops with Unknown or invalid publications. Specified: [zero_<other-app>] | Stale clone values — .env.development still has another app's ZERO_APP_ID / ZERO_APP_PUBLICATIONS (scaffolds are cloned from a sibling app) | Set both to this app (e.g. ZERO_APP_ID=doohportal, ZERO_APP_PUBLICATIONS=zero_doohportal), wipe zero's replica volume (docker volume rm <project>_zero_data), re-migrate, restart zero |
Bind for 0.0.0.0:5433 failed: port is already allocated | Compose using default ports (env files not loaded), colliding with another app's stack | Run via bun scripts, or pass --env-file .env --env-file .env.development so the offset ports apply |
Two container sets (myapp-* and my-app-*) fighting over the same ports | Compose project-name normalization differs; pin it via COMPOSE_PROJECT_NAME in .env.development | docker compose down both, remove stray containers, bring up one clean stack |
WARN … no such table: _zero.tableMetadata during startup is normal — it's the status publisher racing the initial replication sync. Only an "level":"ERROR" that ends in the container exiting is a real failure.Working inside a Takeout app? Each app carries its own .claude/skills/takeout-* patterns (the source of truth for models/queries/routes), plus the aumik-dev-takeout skill for the Aumik overlays. And do real PR work in an isolated worktree off origin/main — aumik-ui main is branch-protected. See Using aumik-skills.
Booking & notifications
Two shared building blocks — platform foundations reused across every product, not one-offs. Tracking epic: aumik-infra#18. The booking/scheduling UX is mocked at the scheduling prototype.
Booking system — we build our own (no Cal.com)
Why not Cal.com: its API + API-key generation is Enterprise-gated even self-hosted (sales-quoted, ≥30-user minimum); only embed + webhooks are free. Not worth a paid EE license for one widget. So aumik-infra#5 (Cal.com) is closed and cal.* can be decommissioned.
Design
- Centralized in
aumik-agents— one booking service / DB / API, multi-tenant by{resource}, serving the chat widget, the agent (Level-3 auto-book via Mastra tools), and every product's@aumik/portal. packages/db(Drizzle, ownaumik_agentsDB):resources,availability_rules+availability_overrides,bookings,notifications,notification_prefs.packages/booking— pure service: availability (UTC-stored, TZ/DST-correct), a DB-level double-booking guard (EXCLUDE USING gistovertstzrange),.icsgeneration.apps/api— Hono REST alongside Mastra:GET /api/scheduling/{resource}/availability,POST …/book,PATCH …/bookings/{id}(reschedule),POST …/cancel.- No external calendar sync (no Google/Outlook two-way) — deliberate scope cut.
.icslinks only; revisit with Nylas/Cronofy only if customers demand it.
Get-it-right list: UTC + timezones/DST · DB-level conflict guard + idempotent book · reschedule/cancel state machine.
Notifications — engine is Novu (decided)
Channels: WhatsApp (Meta Cloud API) + SMS (gateway, default MSG91) are primary; email = Resend (domain aumik.co) for OTP + confirmations/fallback.
- One engine for email + SMS + WhatsApp across all products: built-in preferences, digests / retries / delivery logs, an in-app
<Inbox/>for@aumik/portal, and workflows-as-code (Novu Framework, fits Mastra). Adoption tracked inaumik-infra#19. - Integration:
comms-manageremits booking domain events (booking.confirmed,booking.reminder,booking.cancelled) → Novu workflows fan out by channel and honor per-user prefs. Novu's delay/digest handles 24h/2h reminders (no hand-rolled queue for notifications). - Self-host footprint on the box: API + Worker + WebSocket + Dashboard + MongoDB + Redis, co-tenant under Uncloud.
Voice calling agent
A self-hosted AI agent that places and answers real phone calls (PSTN) — reminders, scheduling, reception, later marketing. One shared voice layer across products, not a per-product dialer. Tracking epic: aumik-infra#64.
Platform — dograh (decided)
We self-host dograh (BSD-2, open source; the self-hosted Vapi/Retell alternative). Docker stack: api + ui + workers + Pipecat media pipeline, with its own Postgres/Redis/MinIO.
- Why: no per-minute SaaS lock-in; calls, recordings and transcripts stay on our infra; BYO LLM/STT/TTS (Claude for reasoning, Deepgram STT, Cartesia/Sarvam TTS); built-in telephony (Twilio, Vonage, Plivo, Telnyx…), human call-transfer, voicemail detection, and a visual workflow builder.
- Not the same as widget voice: the
<VoiceCallButton/>browser voice (WebRTC + Sarvam, inaumik-widgets) is a separate, complementary track. This page is about phone calls. - Languages: dograh's bundled zero-key stack is English-only. Target is English + Spanish (Hindi as a stretch) — ES/HI need our own provider keys (Deepgram STT covers EN/ES/HI; Cartesia/Deepgram TTS for ES, Sarvam for HI).
How it plugs into the platform
Phone (PSTN) ── telephony provider (trunk)
│
┌────────────── dograh (self-hosted, Docker) ──────────────┐
│ api · builder UI · workers · Pipecat (STT → LLM → TTS) │
└──────┬────────────────────────────────────────┬──────────┘
│ (2) HTTP-API tools: act mid-call ▲ (1) POST to trigger endpoint
▼ │ → outbound call
aumik-agents REST (Hono) Novu workflow "voice" step
GET …/availability · POST …/book ◄── booking.reminder event
PATCH …/bookings/{id} (same bus as WhatsApp/SMS/email)
- (1) Outbound trigger: a dograh
triggernode exposes an API endpoint; the Novubooking.reminderworkflow (see Booking) gains a voice step that POSTs to it. Same event bus, no parallel scheduler. - (2) In-call actions: dograh agent nodes attach HTTP-API tools / webhook nodes that call the booking REST routes — look up, confirm, reschedule — plus call-transfer for human handoff and structured
extractionof intent. (dograh also ships an MCP server, but that's a workspace-management surface for coding agents, not the mid-call tool path.)
Phases
- Phase 0 — local spike done: dograh booted locally, browser Web Call validated end-to-end in English on the bundled zero-key stack (TTS heard, transcript read). Deepgram key sourced for the Spanish turn.
- Phase 1 — platform on the box live: voice.aumik.co behind Caddy (stack in
aumik-infra/voice/, PR#68). Still open from this phase: SSO gate (rides Authentik#63), backups for/mnt/voice/*(recordings = customer data), image pinning, telephony number. - Phase 2 — standalone pilot receptionist live: the Cleaning Receptionist (below) answers, checks real availability, books, and can hand off to a human — for cleaning ops' first customer. v1 scope is browser/app Web Call; Twilio PSTN wiring + first US number is the remaining half. EN live, ES next. Tracked in
aumik-infra#69. - Phase 3 — integrated beachhead (after booking
#18lands): reminder calls triggered offbooking.reminderthat read + update real bookings by voice, with human handoff. - Phase 4 — marketing calls (gated): only after compliance work — consent capture, DNC/opt-out scrubbing, recording disclosure, quiet hours, rate limits.
Phase 2 as built — the Cleaning Receptionist
The pilot flow running today. An inbound caller reaches the dograh workflow "Cleaning Receptionist" on voice.aumik.co; mid-call it calls real booking APIs and writes real calendar events:
Caller — browser Web Call today · Twilio PSTN next
│
dograh workflow "Cleaning Receptionist" (voice.aumik.co)
tools: check_availability · book_appointment · transfer_to_human (→ owner's phone)
│ HTTP-API tools, bearer VOICE_TOOLS_TOKEN
▼ (overlay-internal: agents.internal:4111)
aumik-agents (Mastra server.apiRoutes)
GET /scheduling/cleaning/availability · POST /scheduling/cleaning/book
│ CalDAV (zero-dep raw fetch; 409 on taken slots)
▼
Radicale CalDAV — caldav.aumik.co /cleaning-agent/cleaning/ (each booking = an .ics event)
- Workflow-as-code: the workflow + tools live in
aumik-infra/voice/workflows/cleaning-receptionist.json, pushed idempotently withpush.sh(upserts tools, publishes). Booking routes: PRaumik-agents#15. Note Mastra reserves/api/*, hence the unprefixed/scheduling/…paths. - Human handoff:
transfer_to_humanforwards the call to the operator's phone when the caller asks for a person or the agent gets stuck.
Viewing the bookings calendar (cleaning)
Every booking the agent makes lands as an event in the self-hosted Radicale calendar. Subscribe once and it stays live on Mac/iPhone:
- Apple Calendar → add a CalDAV account, Account Type Advanced:
server
caldav.aumik.co· usercleaning-agent· port443+ SSL · Server Path/cleaning-agent/. - ⚠ The path is the principal, not the calendar: entering
/cleaning-agent/cleaning/fails with "No calendar home was specified". Point at/cleaning-agent/and thecleaningcalendar is auto-discovered. - Password:
ICAL_CLEANING_AGENT_PASSWORDin the gitignoredaumik-infra/ical/.env(ask Apijay). Quick checks without a client: Radicale's web UI atcaldav.aumik.co/.web/, or an authedGETon the collection returns the whole calendar as.ics.
Status & open decisions
Status (2026-07-04): platform live at voice.aumik.co (dograh v1.40.0, isolated stack under Uncloud — runbook in aumik-infra/voice/README.md), and the Cleaning Receptionist is live: web-call bookings verified end-to-end in English, first real event landed in the cleaning calendar. Next: Spanish turn (Deepgram STT + ES-capable TTS), then Twilio wiring + first US number.
- Owner — Phase 1 (platform) was built out with Deepak; Phase 2 (
aumik-infra#69) is Apijay's, solo, as of 2026-07-04. Aniket reviews per the Quality Gate. - Beachhead — reminder/booking calls to our own customers (lowest regulatory risk); language target EN+ES points at the US cleaning market.
- Telephony provider — decided: Twilio (US-lead, EN/ES). Wiring + first number tracked in
aumik-infra#69.
Infrastructure & deploy
One shared box runs everything. Know how it's laid out — and the rules that keep it alive — before you deploy.
The shared box
A single Hetzner server runs everything via Uncloud (lightweight Docker orchestration). All apps are co-tenants on it. Shared services, defined in aumik-infra/compose.yaml and deployed with uc deploy -y:
- Postgres 16 + pgvector — shared DB server; each app gets its own DB + role (
aumik_hospitality,aumik_food,aumik_law,infisical, …). - Redis 7 — shared cache + queue backbone (BullMQ jobs).
- Infisical — secret store (see Secrets).
- Caddy — reverse proxy + automatic TLS.
Other co-tenant services: Postiz (social scheduler), Radicale (CalDAV/CardDAV, caldav.aumik.co), prototypes (proto.aumik.co), and this handbook (docs.aumik.co). Cal.com is being decommissioned (see Booking).
Domains
*.aumik.co for any Aumik service (e.g. proto.aumik.co, docs.aumik.co). The cluster also has a *.uncld.dev address, but it's a volatile fallback that changes whenever the cluster is re-initialized — never hardcode it as a published URL.Hard rules — read before touching the box
uc machine init against the box. It re-initializes the cluster and wipes orchestration state (this has caused a production outage).🚫 Never
uc deploy --recreate on the shared infra compose. It corrupts the shared Postgres WAL for every app.⚠ App data lives in the shared Postgres. Confirm backup status (
aumik-hospitality#4) before any destructive operation.✅ App deploys are co-tenant: own image / services / subdomain / DBs; never touch shared state beyond adding your own DB.
Deploying an app
- Tier-B apps: the CI contract centers on a single orchestration command,
bun ops release(build → deploy → migrate), with secrets injected from Infisical. Reference:aumik-hospitality(live Tier-B + CI/CD reference). Copy its shape rather than inventing one. - Static sites (this handbook, the prototypes): an
nginx:alpineco-tenant serves a/srv/<name>dir; the repo is rsync'd up and the service redeployed via Uncloud. One-liner deploy lives in each repo (e.g../docs-site/deploy.shinaumik-infra). - Shared infra changes: from the
aumik-infrarepo, source the gitignored bootstrap.env, thenuc deploy -y(never--recreate) anduc caddy deployfor DNS + TLS.
Disaster recovery
- Cluster wiped, data survived (common case): run the infra recovery script — it validates secrets, aborts if the Postgres data dir is gone, reserves a domain, deploys without
--recreate, and health-checks. SeeRECOVERY.mdinaumik-infra. - Box totaled (no surviving data): blocked until DB backups land (
aumik-hospitality#4).
Secrets
All secrets live in Infisical (self-hosted on the box). Never hard-code them; never paste them into Slack or issues.
Pulling secrets
Use the aumik-ops-secrets-pull skill (wraps the Infisical CLI) to generate a local .env / .env.production for deploys:
aumik-ops-secrets-pull <project-slug> <env> [<out-path>]
It needs the machine-identity credentials (set as env vars); project IDs are cached locally.
What lives where
- The shared app-secrets project (Development env) holds shared keys — e.g. Resend (email; sending domain
aumik.co, verified), OpenRouter (LLM routing for widgets/agents), Postiz DB. Notification provider creds (Meta WhatsApp, SMS gateway) live here too once wired. - Bootstrap secrets that Infisical itself depends on can't live inside Infisical (chicken-and-egg). They live only in a gitignored
.envin theaumik-infrarepo, mirrored to 1Password + iCloud Keychain. See the infra repo for the exact set and rotation steps.
uc deploy by sourcing the infra .env into the shell (the pinned Uncloud version has no uc secret subcommand). Everything else is pulled from Infisical at deploy time.DevOps
Operational reference for the things that keep services reachable — DNS first. Add ops runbooks here as they stabilize.
DNS — aumik.co
DNS is hosted at WordPress.com (ns1/ns2/ns3.wordpress.com). As of 2026-07-04 records are scriptable via the WordPress.com MCP connector (Apijay's Claude session; first use was the voice record) — the panel remains as fallback. Changes still go through the org owner (Apijay); devs don't have panel or MCP access. Every box-served hostname is an A record to the Hetzner box 178.105.125.211.
| Host | Type | Value | Serves |
|---|---|---|---|
mail.aumik.co | A | 178.105.125.211 | Stalwart mail (SMTP/IMAP) |
webmail.aumik.co | A | 178.105.125.211 | Roundcube webmail (shared social inbox) |
postiz.aumik.co | A | 178.105.125.211 | Postiz social scheduler |
infisical.aumik.co | A | 178.105.125.211 | Infisical secrets (canonical host) |
proto.aumik.co | A | 178.105.125.211 | Clickable prototypes |
docs.aumik.co | A | 178.105.125.211 | This handbook |
hospitality-staging.aumik.co | A | 178.105.125.211 | Hospitality staging (auto-deploy pilot) |
voice.aumik.co | A | 178.105.125.211 | dograh voice-calling platform |
alumni.aumik.co | A | 178.105.125.211 | Alumni app |
aumik.co | MX | 10 mail.aumik.co. | Inbound mail → Stalwart |
aumik.co | TXT | v=spf1 include:_spf.resend.com ~all | From-domain SPF (Resend) |
send.aumik.co | TXT | v=spf1 include:amazonses.com ~all | Return-path SPF → 10/10 deliverability |
_dmarc.aumik.co | TXT | v=DMARC1; p=quarantine; | DMARC policy |
resend._domainkey.aumik.co | TXT | (DKIM public key) | Resend DKIM signing |
dig +short webmail.aumik.co A @ns1.wordpress.com · dig +short send.aumik.co TXT @ns1.wordpress.comTwo panel gotchas: enter the host label only (
webmail, not the FQDN — it appends the domain), and paste TXT values unquoted (the panel adds its own quotes). If the authoritative NS is empty a minute after saving, it didn't commit.Brand / repo index
What each product is, its stack, and what stage it's at. Stage: live in production · active work in progress · scaffold early/greenfield.
Products
| Product | What it is | Stack | Stage |
|---|---|---|---|
aumik-hospitality | Real-estate / boutique zen-wellness brand + investor tools. CI/CD reference. | Takeout v2 | live |
aumik-astro | Premium self-service astrology portal (single-guru booking, AI-fuelled). Not a marketplace. · prototype ↗ | Vite·React·Shadcn → Takeout | active |
aumik-assistants | Consumer SaaS frontend for sector assistants (law → insurance/medical/home); brain is aumik-agents. | Takeout v2 | active |
aumik-alumni | Connects colleges with alumni; USP is official event photos / memories. · prototype ↗ | Tamagui·Supabase | active |
aumik-cleaning | Self-service marketplace to book cleaning services (customers ↔ providers). | Vite·React·Shadcn | active |
aumik-dev | Move businesses off Joomla/WordPress onto custom AI-enabled builds; GTM via preview sites. · prototype ↗ | Vite·React·Shadcn → Takeout | scaffold |
aumik-food | Vegan/veg tiffin-ordering (aumikfood.com): order flow, milk orders, admin. | Vite·React·Shadcn → Takeout | scaffold |
aumik-dooh | Digital out-of-home: screens + install, CMS (dooh in aumik-ui), TV player (aumik-player in aumik-native). · prototype ↗ | ReNative + web | scaffold |
aumik-health | Clinical-research benchmarking SaaS; benchmark any health activity, public self-enroll. | Takeout | scaffold |
aumik-marketing | Cross-product campaign & ad-creative tracker (one issue per campaign). CRM product moved into aumik-ui. | docs / tracker | active |
Shared layers
| Layer | What it is | Stack | Stage |
|---|---|---|---|
aumik-agents | The brains — Mastra HTTP API (law/insurance/medical/home + shared) + booking service. Apps consume over HTTP. | Bun·Mastra·Drizzle | active |
aumik-ui | Web UI monorepo — all web apps + @aumik/widgets + @aumik/portal. | Bun·Tamagui·Turbo | active |
aumik-native | ReNative monorepo — first app is aumik-player (TV player). | yarn·ReNative | active |
aumik-skills | Shared Claude Code skills + ADLC commands. See Using aumik-skills. | Python | active |
aumik-infra | Shared infra compose (Postgres + Redis + Infisical) on Hetzner via Uncloud. Tooling, not a product. | Docker·Uncloud | live |
aumik-prototypes | Clickable lo-fi wireframes, one per product — the spec the dev team builds from. proto.aumik.co. | static HTML | live |
aumik-docs | This handbook. docs.aumik.co. | static HTML | live |
git remote -v) before pushing or running gh against a repo.Using the aumik-skills repo
Aumik's reusable Claude Code skills and ADLC commands — write them once here, consume them from any brand repo instead of re-implementing per repo.
Find your skills by role
Skills are named aumik-<role>-<task>. The prefix is organization, not access control — find your role's row and ignore the rest.
| Role | Prefix | Skills today |
|---|---|---|
| Dev | aumik-dev-* | aumik-dev-init-brand |
| Content | aumik-content-* | plan-script, scene-to-video, generate-book, song-to-video |
| PO | aumik-po-* | aumik-po-daily-standup |
| Ops | aumik-ops-* | aumik-ops-secrets-pull |
Layout
skills/<full-name>/SKILL.md— the skill itself (required).skills/<full-name>/references/*.md— optional contextual docs (linked from SKILL.md).skills/<full-name>/scripts/*.py— ported Python scripts..claude/commands/<full-name>.md— thin slash-command wrapper.aumik_skills/lib/— shared Python primitives (flux,brand_presets,book_lib,ffmpeg_helpers,secrets).
Consume it from a brand repo
pip install -e ../../aumik-skills
ln -s ../../aumik-skills/skills .claude/skills-shared
# symlink only your role's commands — keeps .claude/commands/ scoped:
ln -s ../../aumik-skills/.claude/commands/aumik-content-*.md .claude/commands/
Skills import from aumik_skills.lib, never from a sibling skill's scripts/.
When does a skill belong here?
- Cross-brand only — a skill lives here if ≥2 brands would benefit. Single-brand skills stay in that brand's own
.claude/skills/. - No brand-specific paths — take paths as arguments; don't assume a repo's layout.
- Defer to the brand's own
CLAUDE.mdfor operating rules — skills here are libraries, not policy.
Claude Code setup
Set up Claude Code the way the team runs it — the same plugins and memory layer, so skills and cross-session memory work out of the box.
1. Install Claude Code
Install the CLI and sign in (see the official Claude Code docs for your platform). Confirm it runs with claude in a repo.
2. Add the plugin marketplaces
Inside Claude Code, use the /plugin command to add the two marketplaces we use:
/plugin marketplace add anthropics/claude-plugins-official
/plugin marketplace add thedotmack/claude-mem
3. Install the plugins
/plugin install superpowers@claude-plugins-official
/plugin install claude-mem@thedotmack
/plugin install context7@claude-plugins-official
/plugin install code-review@claude-plugins-official
/plugin install claude-md-management@claude-plugins-official
- claude-mem — persistent cross-session memory. It captures observations as you work and surfaces relevant ones in later sessions, so Claude remembers prior decisions across the codebase.
- superpowers — the team's skills layer (brainstorming, systematic-debugging, TDD, writing-plans, and more). Claude invokes the right skill for the task automatically.
- context7 — fetches current library/framework docs on demand instead of relying on training data.
- code-review / claude-md-management — PR review and CLAUDE.md upkeep helpers.
4. Confirm settings
Your ~/.claude/settings.json should end up with the plugins enabled, e.g.:
{
"model": "opus[1m]",
"enabledPlugins": {
"superpowers@claude-plugins-official": true,
"claude-mem@thedotmack": true,
"context7@claude-plugins-official": true
},
"extraKnownMarketplaces": {
"thedotmack": { "source": { "source": "github", "repo": "thedotmack/claude-mem" } }
}
}
5. Per-repo: pull in shared skills
For Aumik repos, also wire in the shared skills as shown in Using aumik-skills — that gives you the aumik-<role>-* commands on top of the superpowers skills.
/plugin with no arguments to browse the current marketplace listings.