Skip to main content
← cases

MIU — Market Intelligence Unit

Confidential client platform, in production with development ongoing. A self-hosted multi-branch CRM and an Intelligence Unit behind one login, where an autonomous AI research agent researches markets by country — sandboxed so that it has no access to CRM data and no database connection at all. Next.js 16, MongoDB 8, WebSockets, Docker Compose, and 2,000+ automated tests. Demo available on request.

137API Route Handlers
~50MongoDB Collections
9Production Containers
2,000+Automated Tests
2Locales
92.9%Phone Recovery
Next.js 16React 19TypeScriptTailwind CSS v4shadcn/ui (Radix)MongoDB 8next-intlZodjose (JWT)Argon2WebSockets (ws)TipTapRechartsDocker ComposeVitestPlaywrightHermes Agent

Confidential

MIU is a client project. It is in production and development is ongoing. The client, the deployment, and the data stay private, so this case study covers the architecture and the decisions only. There is no public link or repository; a demo is available on request.

The Problem

MIU is a market-intelligence platform in two halves behind one login. The first half is a multi-branch CRM: leads, companies, contacts, per-team pipelines, tasks, handoffs, reports, and notifications. The second half is the Intelligence Unit, where an autonomous AI research agent researches markets by country and returns raw findings plus a reviewed knowledge base of sourced claims.

The platform is built to be deployed self-hosted and white-labeled. It runs as 9 production containers under Docker Compose, with a reverse proxy in front for automatic TLS.

The hard constraint is the agent. A research agent that reads the web will eventually read prompt-injection text. The system has to be designed so that when that happens, the worst case is bad research — never a change to customer data.

The Wall

The agent sandbox is the core of the design:

  • No CRM data access. Intelligence lives in a separate MongoDB database, with credentials scoped to that database alone.
  • No database connection at all. The agent container is not on the database network. It reaches the app only over HTTP on a private network.
  • A hardened container. No published ports, all Linux capabilities dropped, no-new-privileges, a tmpfs /tmp, no Docker socket, and CPU, memory, and PID limits.
  • No lead creation. The agent never creates CRM leads. A person promotes candidates under their own permissions.

Agent API keys are separate from user sessions. They are SHA-256 hashed at rest, individually revocable, and scoped with a small dedicated vocabulary — no wildcard and no delete scope. Every call is audit-logged. Requests are rate limited by IP before authentication and per key after authentication. Secrets that appear in agent output are masked on store and on read.

The CRM

Every query carries a branch filter, injected at the repository layer. An actor with no branch is refused. A custom ESLint rule forbids direct database access outside the repository layer, so the filter cannot be bypassed by a route that talks to MongoDB on its own.

Access control is role-based, with a lead-approval gate. Sessions use refresh-token family revocation, and changes are recorded in an audit trail. Real-time updates run over a WebSocket stream server, which replaced an earlier SSE design. Background work goes through a job queue with atomic claims, with separate CRM and intelligence workers.

The interface is bilingual through next-intl: one left-to-right and one right-to-left locale.

Legacy Migration

About 10,000 legacy rows from spreadsheets and a CSV export were migrated into ~2,300 organizations and ~6,900 contacts. The transform ran in two reviewed stages. The load is idempotent and reversible by batch, with provenance fields on every record.

The dedupe rule is strict: only a mobile number or an email identifies a person — never a landline — and merges happen only within one company. A phone-recovery ladder recovered 92.9% of dialable values. Numbers that could not be verified are flagged rather than guessed.

Backups

Backups are encrypted to a public key, so the server holds no decryption key. An encrypted off-site copy is kept, and restores are covered by documented restore drills.

Testing

The system is covered by 2,000+ automated test cases: about 1,980 unit and integration tests, 68 Playwright end-to-end tests, and 48 security tests that run against the real HTTP stack. Every endpoint requires two denial tests — one for another branch and one for a missing permission.

A single check gate runs lint, typecheck, tests, and build, and a Husky pre-commit hook runs it before code lands.

Status

MIU is in production and development is ongoing. Because the work is confidential, there is no public link.