The shell — bisonsh
bisonsh is an interactive REPL with a Mongo-like statement grammar, multi-line input, history, colored output, and a scriptable batch mode. It talks to a running bisond over the wire protocol — it never opens data files directly.
bisonsh [--connect host:port] [--no-color] [--no-banner]
[--username <user>] [--token <token>]
[--tls] [--tls-ca <pem>] [--tls-pin <sha256>] [--tls-insecure]
[--eval '<stmt>[; <stmt>...]'] [-f script.bsh]Default server: 127.0.0.1:27027. With --eval, -f, or piped stdin the shell runs non-interactively and exits non-zero on the first error. Every flag is listed in the CLI reference; the auth and TLS flags are summarized under Authentication below.
Statement grammar
JSON arguments are relaxed: unquoted keys ($gt included), single-quoted strings, and trailing commas all work. Statements with unbalanced brackets continue on ... lines; a blank line cancels the pending statement.
Reading
bisondb> db.zips.find() // {} filter implied
bisondb> db.zips.find({state: 'NY'})
bisondb> db.zips.find({pop: {$gte: 40000}}).limit(5).skip(10)
{ "_id": {"$oid": "5c8eccc1caa187d17ca6ed16"}, "city": "ALPINE", ... }
returned 5 in 1.2 msChainable modifiers — .limit(n), .skip(n), .explain() — appear in any order, each at most once. After 100 documents the interactive pager prompts -- more (Enter) / q --.
bisondb> db.zips.count({state: 'NY'})
1596Explain
bisondb> db.zips.find({pop: {$gte: 40000}}).explain()
{ "plan": "index_range", "index": "pop", "docsExamined": 1015, "docsReturned": 1015 }
index_range on "pop" — examined 1015, returned 1015The summary line is the planner's verdict in one sentence; the query-engine page decodes every field.
Writing
bisondb> db.people.insertOne({name: 'ada', tags: ['math', 'computing']})
bisondb> db.people.insertMany([{a: 1}, {a: 2},])
bisondb> db.people.updateOne({name: 'ada'}, {$set: {born: 1815}})
{ "matched": true, "modified": true }
bisondb> db.people.deleteMany({a: {$lt: 2}})
{ "deletedCount": 1 }Updates are $set-only — the protocol has no field-removal operator.
Indexes and admin
bisondb> db.people.createIndex('born') // or ({born: 1}), Mongo-style
bisondb> db.people.getIndexes()
_id
born
bisondb> db.people.dropIndex('born')
bisondb> db.people.compact()
bisondb> db.people.drop()
bisondb> show collections
bisondb> show status // serverStatus, pretty-printed
bisondb> help
bisondb> exitAuthentication (auth)
A running bisond requires you to log in before any data command. Connect as a user — the password is prompted (no echo) or read from $BISONDB_PASSWORD; never pass it in argv:
bisonsh --connect 127.0.0.1:27027 --username admin # prompts for the password
BISONDB_TOKEN=… bisonsh --connect 127.0.0.1:27027 # resume a session by tokenOnce connected, manage the session and accounts with the auth statements:
bisondb> auth login <user> // prompt for password and log in
bisondb> auth whoami // show the current user and roles
bisondb> auth passwd [<user>] // change a password (your own needs the old one)
bisondb> auth create-user <u> [role…] // admin: create a user (default role: read)
bisondb> auth list-users // admin: list users and roles
bisondb> auth logout // end the session
bisondb> auth bootstrap <user> // first-run: create the first admin from the tokenRoles are read, readWrite, and admin; a command you lack the capability for returns E[Forbidden]. The complete model — Argon2id hashing, tokens, first-run bootstrap, and the role→capability table — is on the Security page.
TLS
To connect over an encrypted transport, add a TLS flag matching how the server's cert is trusted: --tls (system trust store), --tls-ca <pem> (a specific self-signed/CA cert), --tls-pin <sha256> (a pinned fingerprint), or --tls-insecure (dev only — the banner then shows ENCRYPTED but UNVERIFIED). The banner's transport line tells you which you got:
bisonsh --connect localhost:27027 --tls-ca ./tls/cert.pem --username adminConnecting with the wrong transport (plaintext to a TLS server or vice-versa) fails fast with a message telling you to add or drop --tls.
Error reporting
Parse errors show a caret diagnostic and never end the session:
bisondb> db.students.find({cgpa: {$gt 3.5}})
db.students.find({cgpa: {$gt 3.5}})
^ expected ':' after key '$gt'Server errors carry their protocol code:
E[DuplicateKey] duplicate _id: 507f1f77bcf86cd799439011If the connection drops, the next statement attempts one transparent reconnect before reporting a network error.
Scripting
bisonsh --eval "db.t.insertOne({x: 1}); db.t.count()"
bisonsh -f setup.bsh
cat queries.bsh | bisonshStatements split on top-level ; (semicolons inside strings or brackets are safe). The first failing statement stops execution with exit code 1 — usable in CI.
History and display
- History persists to
~/.bisonsh_history(capped at 1000 entries). - Output is colorized on TTYs with truecolor, 256-color, and 16-color fallbacks detected from
COLORTERM/TERM;--no-colororNO_COLORdisables it. - The startup banner appears only in interactive TTY sessions (
--no-bannerto suppress;BISONDB_ASCII=1forces a plain-ASCII variant).
