Skip to main content

Architecture

Four product binaries in one Go workspace. They are deployed separately, own their schemas outright, and never call each other's databases.

platform/         the one shared Go module (auth, db, config, email, waitlist)
apps/
nn_converse/ autonomous voice agent
nn_intel/ post-call batch analysis
nn_assist/ real-time in-call coaching
nn_coach/ simulated-call training & scoring

Each apps/<name> and platform/ is its own Go module. go.work at the repo root wires them together for local development only — every app's go.mod carries its own replace pointing at platform, so no module needs the workspace file to build.

What each binary does​

nextneural_converse​

An AI agent that holds the phone call itself. Inbound calls are matched to a flow by the number dialled; outbound campaigns dial a contact list on a schedule. The media stream runs over a WebSocket to the telephony provider, with speech recognition, an LLM, and speech synthesis in the loop.

Built around: agents (the persona and voice) → flows (inbound line or outbound campaign) → customers → calls.

nextneural_intel​

Batch analysis of call recordings that already happened. You upload audio, it transcribes, runs extraction against configurable fields, and scores the call. Results roll up into a dashboard, a leaderboard, and scheduled reports.

Built around: projects → uploads → calls (each an analysed recording) → customers and segments.

nextneural_assist​

Live coaching while a human agent is on the phone. It transcribes the call in real time and pushes prompts to the agent's screen over a WebSocket.

Built around: agents (human salespeople) → customers → live calls, with a knowledge base backing the suggestions.

nextneural_coach​

Practice calls against a simulated customer. A persona and a scenario define who the trainee is talking to; a run is one practice call, scored against a rubric. It can call nextneural_intel to have a simulated run judged by the same extraction a real call gets.

Built around: personas + scenarios → runs → scores.

What they share​

Everything in platform/ is identical across the four binaries:

PackageWhat it provides
platform/authJWT sessions, API keys, scopes, the role hierarchy
platform/otpsignupEmail OTP signup
platform/orgOrganization setup, bootstrap superadmin
platform/waitlistApproval gating for new organizations
platform/emailInvite and password-reset delivery
platform/dbConnection pooling and migration running
platform/configDeployment mode, superadmin domains

This is why Authentication, Accounts, Organizations, API keys, and Errors are documented once rather than four times.

The request pipeline​

Every org-scoped route runs the same middleware chain, in this order:

RequireAuth / RequireAuthOrAPIKey   ← credential is valid
↓
ResolveCaller ← role and email read fresh from the DB
↓
RequirePasswordSet ← invited users must set a password first
↓
RequireOrg ← 403 needs_org_setup if there is no org
↓
requireWaitlistApproval ← 403 needs_waitlist_approval if pending
↓
requireAppAccess ← org must be entitled to this app
↓
RequireRole / RequireScope ← per-route, where applicable

Two details worth knowing:

Role and waitlist status are read from the database on every request, not from the token. An admin approving an organization takes effect on the caller's very next request rather than whenever their token is next reissued.

RequireRole refuses API keys outright. Anything gated on a role is a human-admin action by design — a key that could mint further keys would make revocation meaningless.

Databases​

Each binary owns one Postgres database, with no shared tables and no cross-app migrations. Migrations apply themselves at boot: every main.go calls db.Migrate(cfg.DatabaseURL) before it starts serving, so pointing DATABASE_URL at an empty database is all it takes to create the schema.

make build-migrations APP=<name> is a compile-time step, not a database one — it copies db/migrations/*.sql into internal/db/migrations/ so go:embed can bake them into the binary.

Not in this workspace​

The on-prem license agent (nn_admin) and the cloud licensing control plane (nn_super_admin) live elsewhere. None of the four binaries here contains license-check code: they are supervised from outside the process.