Console Recovery New in 1.0.4
Two things can lock you out of a QUBIQ installation entirely: losing every administrator password, and losing the key that decrypts the configuration database. Both have a recovery path, and both are run at the server console rather than through the interface.
These commands open the data directory directly. They start no service and bind no port, so they still work on an installation that will not boot — which is the one whose owner needs them.
QUBIQ has no back door and no support-issued unlock code. Recovery is proof of local control: if you can open the data directory, you can recover. If you cannot, nobody can — including us. That is the same trade every comparable product makes, and it is why the recovery key below is worth creating on the day you install.
What this needs
An operating-system account on the server that can read the QUBIQ data directory. No QUBIQ sign-in is involved — the sign-in is what has been lost.
Somebody who can already open app.db and its key can read every credential the system holds, so
handing them an administrator account grants no capability they did not already have. What can be
controlled is visibility, and it is: see What a reset leaves behind.
Recovering an administrator password
1. Find the account
qubiq admin list-admins
USERNAME EMAIL ROLE
maintenance maint@example.com admin
jhalloran j.halloran@example.com superadmin
Only admin and superadmin accounts are listed, and only those can be reset. An engineer's or an operator's account is deliberately out of scope — a console foothold should restore access to the machine, not let somebody assume a named person's identity.
2. Reset it
qubiq admin reset-password --user jhalloran
The first run resets nothing. It prints a file to create:
About to reset the password for "jhalloran".
To confirm, create this file and run the command again within 10m0s:
C:\ProgramData\QUBIQ\data\.reset-approve-20260908-141205
Create the file, run the command again, and the reset happens.
Anyone who can run the command can also create the file, and the product says so rather than implying otherwise. What it buys is deliberateness — a mistyped shell line or a stray script stops here — and a second artifact showing a human meant it. It is single-use and expires after ten minutes, so a file left lying around cannot silently authorise a later reset.
For scripted recovery, --confirm skips it.
3. Use the temporary password once
Password reset for "jhalloran".
Temporary password: hTq7vRnKpZ4mWxFs2dLb
This is shown ONCE and is not stored anywhere. The account must choose a new
password at next sign-in, and every existing session for it has been signed out.
The reset has been recorded in the audit log.
The temporary password is generated and printed, and the product never writes it down — not to
the database, not to a log. There is deliberately no --password flag: an argument would land in
shell history, in ps output and in process-accounting logs.
Sign in with it and QUBIQ requires a new password before the account can do anything else. That is
enforced by the server, not merely asked of the browser: while a password change is owed, the
account may reach exactly three endpoints — change password, sign out, and read its own profile.
Everything else answers 403 PASSWORD_CHANGE_REQUIRED.
What a reset leaves behind
A reset cannot be made impossible, so it is made loud:
| Record | Where | Notes |
|---|---|---|
A warn system-log row | Logs → System | Names the account and the OS user who ran the command |
| An audit entry | The audit journal | Tamper-evident, external, and outlives the installation |
| A security notice | GET /api/security/notices — needs View the audit journal | Kept for seven days |
The notice is stored separately from the system log because the system log is pruned to a ceiling — on a busy installation the one row that matters is exactly the one a burst of traffic evicts.
There is no dismiss. A notice anyone can clear can be cleared by whoever performed the reset, which is precisely the person it exists to expose. Notices age out on their own after a week instead: nobody can silence one early, and nobody has to remember to clean up.
Every existing session for that account also ends immediately — a token issued before the password changed is refused from the next request onwards. That applies to an ordinary password change too, not only to a console reset.
Deleting the user table no longer reopens the setup wizard. Completion is recorded in two places — the configuration database and a file in the data directory — and either one alone refuses a second setup.
That matters because emptying a table used to be enough: anyone who could write to app.db could
empty the user table and mint themselves a super admin through a public, unauthenticated endpoint,
leaving no trace. Closing that hole is why this command exists — it was also the only way back in
after a lost password.
The database encryption key
app.db is encrypted at rest with a key bound to this machine — a DPAPI-sealed blob on Windows, a
key file sealed by systemd-creds or the Keychain where those are available on Linux and macOS.
There is no plaintext ⇄ encrypted migration. If the machine can no longer unseal that key, the database is unreadable permanently. Reaching that state needs no attacker:
- the service account is changed
- a Windows profile is rebuilt
- the machine is re-imaged and the data directory restored
- the disk is moved into a replacement chassis
The database recovery key
The escape hatch is a recovery key: the same key bytes, sealed a second time under a code a person can write on paper.
qubiq db-key create
Recovery key created.
K7M4-9PQR-2XVW-6TYB-3HJN-8CDF-5GKS-4ZAE
Write this down and keep it somewhere that is NOT this machine.
It is shown once and cannot be recovered from the server. The code is generated rather than chosen, so its strength does not depend on whoever typed it, and it is forgiving on the way back in: lower case, spaces instead of dashes, and a trailing newline from a paste all still verify.
| Command | Does |
|---|---|
qubiq db-key create | Generates the recovery key and prints it once. Refuses to overwrite an existing one — replacing it would silently invalidate a code already filed |
qubiq db-key verify | Confirms a code still opens this installation's key. Run it before you file the code |
qubiq db-key show | Prints the database key itself, given the recovery key |
qubiq db-key rewrap | Re-seals the key so a service-account change cannot orphan it |
The code is read from a prompt rather than an argument, for the same reason the password reset has
no --password. --code exists for scripted recovery and says so.
show asks for a code you would not otherwise needAnyone running it is already on the host and could resolve the key directly. Asking proves you hold the thing you will need on a different machine — which turns "we wrote it down somewhere" into a fact you have just verified, rather than an assumption you test during an outage.
Creating a recovery key never changes the key and re-encrypts nothing. Nothing about it participates in opening the database, so the worst outcome of a problem with it is that recovery does not work — which is where an installation without one already is.
rewrap is safe to run at any time and as often as you like: the key does not change, only the
envelope around it, and the replacement is proven to open before anything is written. The supervisor
already does this on boot; the command exists for the machine where the supervisor will not start.
What to keep off the machine
A recoverable deployment needs all of these, stored somewhere that is not the server:
| Item | Recovers |
|---|---|
| A passphrase-encrypted backup | Everything, onto a fresh installation |
ENCRYPTION_KEY (or .env) | Connection secrets in place |
| The database recovery key | app.db itself, when this machine's key store is gone |
| Audit database and QuestDB backups | History and accountability |
Test them. A recovery key that has never been through qubiq db-key verify is a hypothesis.