Skip to main content

Running a binary

Prerequisites​

  • Go 1.26.1 — every module declares this exact version
  • PostgreSQL 14+, reachable at DATABASE_URL
  • Node 20+, for each app's bundled fe/
  • pgvector for knowledge-base search. Optional for nextneural_assist and nextneural_converse (the migration tolerates it being missing and search is simply unavailable); required for nextneural_coach, whose migration has no fallback.
  • nextneural_intel only: a C compiler on PATH (CGO_ENABLED=1, for the VAD binding) and ffmpeg on PATH (FFMPEG_BINARY if it lives elsewhere)

Start a binary​

cd apps/nn_converse
cp .env.example .env # fill in DATABASE_URL at minimum
make -C ../.. build-migrations APP=nn_converse
make -C ../.. run APP=nn_converse

Or from the repo root:

make run APP=nn_converse
nextneural_coach uses a different command name

make run defaults to ./cmd/server, which nextneural_coach does not have — its server lives in cmd/nn_coach. Run it with make run APP=nn_coach BIN=nn_coach, or use its own apps/nn_coach/dev.sh, which also starts the front end.

Migrations​

Migrations apply themselves on every boot. Every main.go calls db.Migrate(cfg.DatabaseURL) before serving, so pointing DATABASE_URL at an empty database creates the whole schema on first start. Re-running is safe — only pending migrations are applied.

make build-migrations is a compile-time step, not a database one. Migrations live twice: the canonical .sql files in db/migrations/, and a copy under internal/db/migrations/ that go:embed bakes into the binary (Go's embed directive cannot reach outside its own package directory). make build and make run do this copy for you; running go run directly does not, which is what produces:

pattern migrations/*.sql: no matching files found

Recovering from a failed migration​

If a migration fails partway, golang-migrate marks the schema version dirty and refuses every later boot:

migrate: migrate up: migration failed: ... (details: pq: relation "x" already exists)

Inspect and clear it by hand:

SELECT * FROM schema_migrations;                       -- version, dirty
UPDATE schema_migrations SET version = 1, dirty = false;

Fix whatever the migration collided with before restarting, or the next boot fails the same way.

Configuration​

Read from the environment, with .env as a fallback — real environment variables always win over the file. Point ENV_FILE at another path to use a different one.

Required everywhere​

VariableNotes
DATABASE_URLpostgres://user:pass@host:5432/dbname?sslmode=disable
JWT_SECRETSigning secret for session tokens

Common across binaries​

VariableDefaultPurpose
PORT8080HTTP listen port (nextneural_coach uses NN_COACH_ADDR, default :8010)
APP_BASE_URL—Where the front end is served; used in emailed links
PUBLIC_BASE_URL—Publicly reachable base for telephony callbacks
CORS_ALLOWED_ORIGINS—Comma-separated origins for the browser console
ALLOWED_EMAIL_DOMAINS—Domains that skip the waitlist gate
SUPERADMIN_EMAIL_DOMAINS—Domains whose accounts are platform superadmins
SUPERADMIN_EMAIL / SUPERADMIN_PASSWORD—Bootstrap superadmin account
RESEND_API_KEY / RESEND_SENDER_EMAIL—Email delivery for invites, OTPs, resets
FIREBASE_ENABLED, FIREBASE_PROJECT_ID, …—Enables POST /api/auth/firebase
UPLOAD_DIR./data/uploadsLocal file storage

Without an email provider configured, signup OTPs and invites cannot be delivered — the failure is reported rather than silently swallowed.

nextneural_converse​

VariablePurpose
LLM_PROVIDER, LLM_MODEL, LLM_API_KEY, LLM_BASE_URLConversation model
STT_PROVIDER, DEEPGRAM_API_KEY, SARVAM_API_KEYSpeech recognition
TTS_PROVIDER, NEXTNEURAL_TTS_URL, NEXTNEURAL_TTS_TOKENSpeech synthesis and voice cloning
AWS_*, BEDROCK_*Embeddings for knowledge-base search
VOICE_VAD_*, VOICE_ROUTER_*, VOICE_LANG_LOCK_WORDSTurn-taking and language tuning
PUBLIC_BASE_URLRequired to run a campaign — telephony must reach the webhooks

nextneural_assist​

VariablePurpose
SARVAM_API_KEY, SARVAM_STT_MODELLive transcription
AWS_*, BEDROCK_*Suggestion model and embeddings
SETTINGS_ENCRYPTION_KEYEncrypts stored telephony credentials
AGENT_ASSIST_LIVE_TRANSCRIPTIONToggles live transcription
EXOTEL_SAMPLE_RATEMust match the rate in the Exotel stream URL

nextneural_intel​

Beyond the common set, nextneural_intel needs ffmpeg and a C toolchain at build time. Model selection is configured in-app through model routing rather than by environment variable.

nextneural_coach​

VariableDefaultPurpose
NN_COACH_ADDR:8010Listen address
NN_COACH_DATABASE_URLfalls back to DATABASE_URLDatabase
OPENROUTER_API_KEY, OPENROUTER_BASE_URL—Model access
NN_COACH_AGENT_MODEL, _PERSONA_MODEL, _JUDGE_MODEL, _BRIEF_MODEL—Per-stage model choice
SARVAM_API_KEY, NN_COACH_SARVAM_VOICE, NN_COACH_TTS_PROVIDER—Voice practice
JUDGE_API_BASE_URL, JUDGE_API_TOKEN—Delegates scoring to nextneural_intel
VOICE_API_BASE_URL, VOICE_API_TOKEN—Automated voice runs against a deployed flow
NN_COACH_DB_AUTO_CREATEtrueCreates its own database at boot if missing

NN_COACH_DB_AUTO_CREATE needs the database role to hold CREATEDB. Without it, create the database yourself:

sudo -u postgres psql -c "CREATE DATABASE nn_coach OWNER st_user;"

Health checks​

BinaryEndpoint
nextneural_converse, nextneural_intel, nextneural_assistGET /api/healthz
nextneural_coachGET /health

None require a credential. nextneural_intel also reports its build identity, so a deployed machine can be asked what it is actually running:

{ "status": "ok", "version": "1.4.2", "commit": "0f9c19a", "built": "2026-09-18T10:22:31Z" }

Ports​

All four default to 8080 except nextneural_coach (8010), so a host running more than one needs PORT set explicitly per service.

Building for deployment​

make build APP=nn_converse          # local build
make release-linux APP=nn_converse # static linux/amd64 binary
make build-all # every app

Three of the four cross-compile to a fully static binary with no runtime libc dependency. nextneural_intel is the exception — its VAD binding needs cgo.