Configuration
One file: App/config.ts. No .env, no YAML stack, no secret loader. Self-hosters edit one TypeScript object with commented JSON shape — and that's the whole story.
config.ts
The shape, with the same comments you'll see in the file:
export const config = {
// Where all user-generated data lives.
dataDir: "./data",
db: {
// "sqlite" (default) or "postgres".
driver: "sqlite",
sqlitePath: "./data/app.sqlite",
postgresUrl: "",
},
// HTTP port + the URL the app should think it lives at.
port: 8471,
publicUrl: "http://localhost:8471",
// 32+ random bytes. Rotate to log everyone out.
sessionSecret: "change-me-in-production",
// Global SMTP fallback. Per-company EmailProvider rows take precedence.
smtp: {
host: "", port: 587, secure: false,
user: "", pass: "",
from: "Genosyn <no-reply@genosyn.local>",
},
// OAuth client credentials for integrations that need them.
integrations: {
google: { clientId: "", clientSecret: "" },
// ...
},
} as const;Switching to Postgres
Genosyn ships on SQLite by default — single file, zero install. To switch to Postgres, flip the driver and point at a connection URL:
db: {
driver: "postgres",
sqlitePath: "",
postgresUrl: "postgresql://user:pass@host:5432/genosyn",
},All entities and migrations work on both drivers. On startup Genosyn calls AppDataSource.runMigrations() — any pending migrations apply automatically.
The data directory
Everything user-generated — the SQLite file, materialized git checkouts, MCP configs, uploaded attachments — lives under dataDir:
data/
├── app.sqlite
└── companies/<co-slug>/employees/<emp-slug>/
├── .mcp.json
├── repos/<owner>/<name>/
└── ...In the Docker image, this is mounted at /app/data. The installer maps a named volume genosyn-data there — back that volume up and you've backed up everything.
Email transport is per-company: every Company can have one or more EmailProvider rows. Supported transports today:
- SMTP via
nodemailer. - SendGrid, Mailgun, Resend, Postmark — REST-based, paste an API key.
System-level sends (password resets, invites, welcomes) and any company without its own provider fall back to a single global SMTP transport. Configure it in the app at Admin → Email transport: fill in the host, port, encryption, username, password, and from-address, then use Send test to confirm deliverability. The settings are stored in the database and take effect immediately — no restart. Until it's configured, the Admin → Overview and Instance Health dashboards flag Email transport with a warning, because those system emails only log to the server console and never reach a mailbox.
A file-based default also exists: the smtp block in config.ts. The dashboard override takes precedence over it; clearing the override (the Reset button) reverts to whatever config.ts provides, and if that's blank too, to the console. When a global transport is configured either way, adding a company SMTP provider at Settings → Email pre-fills the host, port, encryption, username, and from-address from it — you only enter the password. Every send appends an EmailLog row you can read at Settings → Email Logs.
Secrets
Three places store secrets, each for a different lifecycle:
- sessionSecret
- In
config.ts. Used to sign cookies. Rotating it invalidates every session. - Connection config
- Encrypted per-Connection blobs on
IntegrationConnection.encryptedConfig(AES-256-GCM). Decrypted at tool-call time. - Secret entity
- Free-form encrypted key/value pairs scoped to a company, editable from
Settings → Secrets. Surfaced to Pipelines.
Admin & instance health
Install-wide operations live under the Admin section (your avatar menu → Admin) — separate from a single company's Settings. Because it spans every company on the deployment, it's gated to master admins: instance-level operators, a global flag on the user account that's distinct from the per-company owner / admin / member roles. The first account to sign up on a fresh install is bootstrapped as the master admin; from Admin → Users an existing master admin can grant or revoke the flag on anyone else (you just can't revoke your own, so the install always keeps at least one operator). Since it's operator-only, Admin isn't advertised in the products section menu — reach it from your avatar menu.
- Overview — an at-a-glance dashboard: instance health status, the running version and build, database driver, uptime and memory, and an inventory of companies, members, and AI employees.
- Instance Health — live probes of the deployment substrate: database connectivity and round-trip latency, pending schema migrations, a writable data directory, the backup story, and the email + Web Push transports. This is distinct from a company's
Settings → System Health, which watches that company's routines, models, and integrations. - Migrations — expands the Instance Health migrations probe into the full ledger: every schema migration, its state, and whether the database has drifted from the code. See
Migrationsbelow. - Database — a raw SQL console over Genosyn's own application database. See
Database consolebelow. - Email transport — configure the install-wide global SMTP server for system emails (password resets, invites), with a test send. See
Emailabove. - Sign-ups — an instance-wide toggle for self-service registration. See
Sign-upsbelow. - Users — every human member across every company, with their handle, how many companies they belong to, and which companies they own. Grant or revoke master admin on any user here to control who else can reach this dashboard. Delete an account from here to remove the person and everything scoped to them (memberships, API keys, notifications); content they authored is kept but unlinked. A user who still owns a company can't be deleted until you reassign or delete those companies first, and you can't delete your own account here.
- Companies — every company (tenant) on the instance, with its owner and member + AI-employee counts. Deleting one runs the same cascade as a company's own
Delete companyaction — every employee, routine, message, note, and finance record it owns, plus its files on disk — so an operator can prune any tenant without switching into it first. - Backups — see below.
Sign-ups
Admin → Sign-ups is an instance-wide toggle for self-service registration. Flip Disable sign-ups on and the public sign-up page stops accepting new accounts — anyone who lands on it sees a “sign-ups are closed” notice instead of the form, and the API refuses a registration attempt with a 403. Existing members keep their accounts and can still sign in; this only stops new people from registering themselves.
One account is always exempt: the very first account on a fresh install, so a box with no users yet can never lock itself out before an operator exists. With sign-ups disabled, add people by promoting an existing account to master admin from Admin → Users, or by inviting them into a company from that company's Settings → Members.
Database console
Admin → Database is a raw SQL console wired directly to Genosyn's own application database — the same SQLite or Postgres the app itself runs on. It is meant for operators who need to inspect or repair an install directly: check a row the UI doesn't surface, audit what an AI employee wrote, or fix up data after a botched import. Distinct from Explore, which runs SQL against a company's external database integrations.
- Schema browser — every table with its live row count down the left. Click a table to load a
SELECT *; expand one to see its columns (primary keys flagged) and click a column to drop its name into the editor. - Read-only by default — the console runs one statement at a time and refuses anything that isn't plainly a read. To run an
INSERT/UPDATE/DELETEor DDL you must first flip Allow writes, which surfaces a standing warning — these statements change the live database permanently, so take a backup first if you are unsure. - Results — a scrollable grid with the row count and elapsed time; long result sets are capped (100–5,000 rows, your choice) and flagged when truncated. Recent queries are kept under the
Historytab. Press⌘↵/Ctrl↵to run.
Migrations
Admin → Migrations is a read-only ledger of every TypeORM schema migration — Total / Applied / Pending / Unknown tiles over the full list. Nothing runs from here: boot applies pending migrations automatically, so this is the detail view behind the Instance Health probe.
- Applied — recorded in the database, in the order they ran. Each shows when it was authored, not when it ran — TypeORM's migrations table records no such timestamp.
- Pending — shipped but not applied. A healthy instance has none, so anything pending points at a migration that failed at boot; read that boot's server log.
- Drift — the database disagrees with the code. Unknown is a migrations-table row matching no shipped migration file (a downgrade, or a hand-edited database); out-of-order is an older migration applied after a newer one (usually a branch merge). Take a backup before repairing either.
Backups
A backup zips the entire data directory — every company's rows, uploads, and credentials — so it is install-wide, not per company. Run one from the CLI:
genosyn backup --out ~/backups/genosyn-$(date +%F).tar.gz
genosyn restore ~/backups/genosyn-2026-04-22.tar.gzOr drive it in-app at Admin → Backups: back up now, upload an existing .zip to restore from, download or restore any past archive, and set a recurring schedule (daily / weekly / monthly at a chosen hour) backed by the BackupSchedule row. See CLI reference for the flag list.
A backup is written to a temporary .part file and moved into place only once it is complete, so a .zip in data/Backup/ is always a whole archive — never a half-one left by a container restart or an OOM kill. If a backup is interrupted, History keeps showing it as running and the leftover .part is swept on the next start. Restoring also opens the archive before it touches anything, so a damaged file is refused up front rather than part-way through replacing your data.
Off-box destinations (NAS / remote volumes)
Backups live in data/Backup/ by default — on the same disk as everything else. Add one or more off-box destinations under Admin → Backups → Off-box destinations and every completed backup is mirrored there automatically. Three kinds:
- Mounted path — a filesystem path Genosyn can already write to. Mount your NAS share (SMB / NFS / iSCSI) on the host or bind-mount it into the container, then point the destination at that path (for example
/mnt/nas/genosyn). The kernel handles the protocol; Genosyn just copies the archive. Still the simplest option when you are able to mount the share. - SMB / CIFS — push straight to a Windows or NAS share with no mount required. Enter the host, share, an optional folder within it, and a username, password, and optional domain. For when bind-mounting is not available to you — a locked-down Kubernetes cluster, or a host where you cannot mount CIFS. Genosyn negotiates SMB3 and signs the connection; leave
Encrypt in transiton (the default) so the archive is not readable on your network, and turn it off only for a NAS that predates SMB3. - SFTP / SSH — push to a remote host with no mount required. Enter the host, port, username, and a password or private key. Good for appliance NASes (Synology, QNAP, TrueNAS) that expose SSH but are awkward to bind-mount.
Credentials for the SMB and SFTP kinds are encrypted at rest with the same AES-256-GCM helper used for model API keys, and are never returned to the browser.
Use Test on a destination to confirm it is reachable and writable, toggle Enabled to pause mirroring without deleting it, and use Send next to any archive in History to push an existing backup on demand. Delivery is best-effort: a mirror that fails is flagged on the destination with the error, but never fails the backup itself, which is already safe in data/Backup/.
Retention (deleting old backups)
Left alone, data/Backup/ grows forever. Tick Automatically delete old backups under Admin → Backups → Retention and set a number of days: anything older is deleted. Genosyn checks hourly and again straight after every backup, so a window that lapses at midday is honoured at midday.
Retention is independent of the recurring schedule — it covers every archive in data/Backup/, including ones you made by hand with Back up now, and it runs even when the schedule is off. Two things are always spared, on the principle that a retention setting must never leave you with nothing to restore from:
- The newest completed archive — kept however old it is. If backups stop running for a year, the last one survives. Should that archive turn out not to open, the newest one that does open is kept as well, so an unreadable file can never cost you the last archive you could actually restore from.
- Archives you uploaded through
Admin → Backups— you carried those in from somewhere else and they may be the only copy. Delete them by hand when you are done with them.
Retention is local only. Copies already delivered to an off-box destination stay on the remote — Genosyn never reaches onto your NAS to delete things. Prune those with whatever your NAS or a cron job on that host already offers.
Upgrading
genosyn upgrade pulls the latest image and recreates the container, preserving the data volume. Or rerun the installer:
curl -fsSL https://genosyn.com/install.sh | bashPorts and reverse proxies
The container listens on 8471. Stick a reverse proxy (Caddy, nginx, Traefik) in front of it for TLS and a real hostname. Update publicUrl in config.ts so the app generates absolute links correctly (invite emails, OAuth callbacks).