Server CLI
ntctl is the server-side admin CLI. It talks directly to the same database the Nexterm server uses, so it can do things the web UI cannot: recover a locked-out admin, switch auth providers when SSO is broken, inspect migrations, or run a quick SQL query.
It is not the same tool as nt, which is the end-user CLI for connecting to servers. ntctl is meant to be run on the machine that hosts the Nexterm server.
Installation
If you run Nexterm from source, ntctl is already there. The bin entry in package.json exposes it once you've installed dependencies:
npx ntctl --helpIf you'd rather call it without npx:
npm link
ntctl --helpThe Docker image ships ntctl as /usr/local/bin/ntctl, so you can run it inside a running container:
docker exec -it nexterm ntctl user:listHow it finds your data
ntctl needs the same ENCRYPTION_KEY the server uses, otherwise it can't read encrypted columns. It looks for it in this order:
- The
ENCRYPTION_KEYenvironment variable. - A
.envfile in the data directory. - A Docker secret (
/run/secrets/encryption_keyor similar, handled by the existingsecretsloader). data/encryption.keyrelative to the working directory.
If you keep your data somewhere other than the current directory, pass --data-dir:
ntctl --data-dir /var/lib/nexterm user:listThis works whether the path points at the data directory itself or its parent. Internally it makes sure the runtime sees a data/ folder in the working directory, which is what the server expects.
Command groups
Commands are grouped by area, using a colon separator (user:list, auth:enable, db:status). Typing just the group name prints the commands in that group:
ntctl user
ntctl auth
ntctl dbntctl --help shows everything at once.
User management
Listing accounts
ntctl user:listPrints id, username, full name, role, and whether TOTP is enabled.
Creating an account
ntctl user:create alice --adminWithout --password, you'll be prompted for the password interactively (with confirmation). For scripted use, pass it directly:
ntctl user:create alice --password "hunter2" --first-name Alice --adminFlags:
| Flag | Description |
|---|---|
--password | Password value. If omitted, prompts. |
--first-name | Defaults to the username if not provided. |
--last-name | Defaults to empty. |
--admin | Creates the account with the admin role. |
Resetting a password
ntctl user:reset-password aliceUseful when an admin forgot their password and no other admin can reset it from the UI. Existing sessions stay valid until they expire; ntctl does not force a logout.
Promoting and demoting
ntctl user:promote alice
ntctl user:demote aliceDemoting the last remaining admin is refused; you'd be locking yourself out.
Deleting an account
ntctl user:delete aliceRemoves the account and all of its owned data (folders, identities, sessions), the same cascade the controller does when an admin deletes a user from the UI.
Authentication providers
When SSO is misconfigured and nobody can log in, this is the way back. auth:list shows the current state:
ntctl auth:listEach row has a kind (internal, ldap, or oidc), an id, a name, and whether it's enabled.
Switching providers
ntctl auth:enable internal
ntctl auth:enable my-ldap
ntctl auth:enable 3enable is exclusive: it turns the chosen provider on and turns everything else off. Use internal (or its alias local) for the built-in username/password login. For LDAP, you can use the provider name or its numeric id.
ntctl auth:disable my-ldap
ntctl auth:disable internaldisable is non-exclusive. The CLI refuses to disable internal auth if no other provider is enabled, and if you disable the last enabled non-internal provider, it re-enables internal auth automatically so the instance is never left without a login method.
After enabling or disabling providers, restart the server so the in-memory provider caches refresh.
Database
Status
ntctl db:statusShows the database type, the SQLite file path and size (when applicable), how many migrations have been applied vs. pending, and the account counts. If migrations are pending, they're listed.
Running migrations
ntctl db:migrateApplies any pending migrations. Same code path the server uses on startup, useful if you want to run migrations explicitly before starting the server, or in a CI step.
Inspecting schema
ntctl db:tables
ntctl db:schema accountsdb:tables lists every table. db:schema <table> prints the columns with their type, nullability, default value, and whether they're part of the primary key.
Running SQL
ntctl db:query "SELECT id, username, role FROM accounts WHERE role = 'admin'"SELECT, PRAGMA, SHOW, EXPLAIN, and WITH statements print their results as a table. Everything else prints the number of affected rows.
You can also pipe SQL in from stdin or a file:
cat patch.sql | ntctl db:query -
ntctl db:query - < patch.sqlStatements that start with DROP, TRUNCATE, DELETE, or ALTER prompt for confirmation before running. Pass --force if you're confident and want to skip the prompt (for example in a script):
ntctl db:query --force "DELETE FROM audit_logs WHERE createdAt < '2024-01-01'"The query runs through Sequelize, so it works the same way against SQLite and MySQL.
