Summary
Two related items:
- Bug.
--no-passcode is documented and parsed but never actually
disables the auto-generated passcode. The CLI uses Commander's
.option("--no-passcode", ...) (which conventionally exposes
options.passcode === false) but the code reads options.noPasscode,
which is always undefined. The else-branch always runs and a fresh
passcode is generated.
- Feature request. A
--passcode <value> option that pins the remote
access passcode to a caller-supplied value, so process supervisors
(PM2, systemd, Docker) don't have to re-grep the log on every restart
and update every connected client.
Affected version
kanban@0.1.68 (latest at time of report, published 2026-05-08).
Bug — --no-passcode is dead
Reproduce
# 1. Install
npm install -g kanban
# 2. Run with --no-passcode, bound to a LAN IP (so the remote-host code path
# runs — localhost binding short-circuits the passcode block).
kanban --host 192.168.1.29 --no-open --no-passcode
Observed output:
Binding to host 192.168.1.29.
🔐 Remote access passcode: 7r2h5ZQZ
Share this with users who need access.
Cline Kanban running at http://192.168.1.29:3484/9router
Expected (from the --help):
--no-passcode Disable auto-generated passcode for remote access (for advanced users behind a reverse proxy).
And confirmed by the code itself when the flag did fire:
Passcode authentication disabled (--no-passcode). Ensure you have your own auth layer.
That message never prints.
Root cause (in the installed bin/kanban)
program2
.option(
"--no-passcode",
"Disable auto-generated passcode for remote access (for advanced users behind a reverse proxy)."
)
// ...
if (isKanbanRemoteHost()) {
if (options.noPasscode) { // ← always undefined → falsy
disablePasscode();
console.log("Passcode authentication disabled (--no-passcode). ...");
} else {
const passcode = generatePasscode();
// ...
}
}
Commander's convention for .option("--no-foo", ...) is to expose
options.foo defaulting to true and flipping to false when the flag is
passed. So the check should be !options.passcode (or the option should be
declared as .option("--no-passcode") paired with reading options.passcode).
The current code references a key (noPasscode) that Commander never
populates, so the branch is dead.
Suggested fix (minimal)
- if (options.noPasscode) {
+ if (options.passcode === false) {
disablePasscode();
console.log("Passcode authentication disabled (--no-passcode). ...");
}
And the same swap at the call site that propagates the value:
- noPasscode: options.noPasscode === true
+ noPasscode: options.passcode === false
(Or rename the CLI option to .option("--disable-passcode") and keep
reading options.disablePasscode — explicit and avoids the
Commander negation foot-gun.)
Feature request — --passcode <value>
Why
The current passcode rotates on every process restart. With a supervisor
that restarts on memory/crash (pm2 --max-memory-restart 4G, systemd
Restart=on-failure, Docker healthcheck restart), every restart invalidates
all bookmarked clients and forces the operator to pm2 logs kanban | grep passcode, copy a new code, and redistribute it to every device that uses
the board.
A stable passcode supplied by the operator solves this without sacrificing
the "remote access requires a shared secret" property the auto-generated
passcode provides — it just moves the secret out of "process-lifetime
ephemeral" and into "operator-managed".
--no-passcode does exist, but turning auth off entirely on a LAN-bound
service is a bigger compromise than most operators want — especially when
the service hosts coding agents that can execute arbitrary commands. A
fixed passcode is the middle ground.
Proposed UX
kanban --host 192.168.1.29 --no-open --passcode "$KANBAN_PASSCODE"
--passcode <value> (or --passcode-env <ENV_VAR> for not putting the
value on the CLI). Mutually exclusive with --no-passcode.
- Behaviour: bypass
generatePasscode() entirely, install the supplied
string as the passcode, print the usual 🔐 Remote access passcode: …
banner (so PM2 logs still document what to share).
- Validation: enforce a minimum length, e.g. ≥ 8 chars, to discourage
weak choices. (The current auto-generated codes are 8 alphanumeric
characters, so matching that as the floor seems right.)
- Optional: an env var alternative (
KANBAN_PASSCODE) so the value
doesn't appear in ps. Common pattern with systemd EnvironmentFile=
and Docker env_file:.
Why not --no-passcode + reverse proxy
That's the pattern the current --no-passcode docstring recommends, but
it requires the operator to:
- Stand up an extra TLS-terminating proxy in front of every kanban
instance.
- Implement and configure its own auth (basic auth, OAuth, mTLS, etc.).
- Ensure nothing else on the LAN can reach the kanban port directly,
since kanban itself is now wide open.
For small self-hosted setups (one workstation behind a NAT, used by a
single developer or a 2–3-person team), a shared static passcode is much
closer to what the operator actually wants.
Environment
- Node
v24.16.0 (nvm-managed)
- macOS Darwin (Apple Silicon, but reproduces independently of OS)
- Process supervisor: PM2 7.0.1 (also reproduces when run directly,
e.g. kanban --host 192.168.1.29 --no-open --no-passcode)
- Both
localhost and LAN-IP binding tested — the LAN binding is the
case that should respect --no-passcode and doesn't.
Happy to send a draft PR for the bug fix and/or the --passcode option
if helpful.
Summary
Two related items:
--no-passcodeis documented and parsed but never actuallydisables the auto-generated passcode. The CLI uses Commander's
.option("--no-passcode", ...)(which conventionally exposesoptions.passcode === false) but the code readsoptions.noPasscode,which is always
undefined. The else-branch always runs and a freshpasscode is generated.
--passcode <value>option that pins the remoteaccess passcode to a caller-supplied value, so process supervisors
(PM2, systemd, Docker) don't have to re-grep the log on every restart
and update every connected client.
Affected version
kanban@0.1.68(latest at time of report, published 2026-05-08).Bug —
--no-passcodeis deadReproduce
Observed output:
Expected (from the
--help):And confirmed by the code itself when the flag did fire:
That message never prints.
Root cause (in the installed
bin/kanban)Commander's convention for
.option("--no-foo", ...)is to exposeoptions.foodefaulting totrueand flipping tofalsewhen the flag ispassed. So the check should be
!options.passcode(or the option should bedeclared as
.option("--no-passcode")paired with readingoptions.passcode).The current code references a key (
noPasscode) that Commander neverpopulates, so the branch is dead.
Suggested fix (minimal)
And the same swap at the call site that propagates the value:
(Or rename the CLI option to
.option("--disable-passcode")and keepreading
options.disablePasscode— explicit and avoids theCommander negation foot-gun.)
Feature request —
--passcode <value>Why
The current passcode rotates on every process restart. With a supervisor
that restarts on memory/crash (
pm2 --max-memory-restart 4G, systemdRestart=on-failure, Docker healthcheck restart), every restart invalidatesall bookmarked clients and forces the operator to
pm2 logs kanban | grep passcode, copy a new code, and redistribute it to every device that usesthe board.
A stable passcode supplied by the operator solves this without sacrificing
the "remote access requires a shared secret" property the auto-generated
passcode provides — it just moves the secret out of "process-lifetime
ephemeral" and into "operator-managed".
--no-passcodedoes exist, but turning auth off entirely on a LAN-boundservice is a bigger compromise than most operators want — especially when
the service hosts coding agents that can execute arbitrary commands. A
fixed passcode is the middle ground.
Proposed UX
kanban --host 192.168.1.29 --no-open --passcode "$KANBAN_PASSCODE"--passcode <value>(or--passcode-env <ENV_VAR>for not putting thevalue on the CLI). Mutually exclusive with
--no-passcode.generatePasscode()entirely, install the suppliedstring as the passcode, print the usual
🔐 Remote access passcode: …banner (so PM2 logs still document what to share).
weak choices. (The current auto-generated codes are 8 alphanumeric
characters, so matching that as the floor seems right.)
KANBAN_PASSCODE) so the valuedoesn't appear in
ps. Common pattern with systemdEnvironmentFile=and Docker
env_file:.Why not
--no-passcode+ reverse proxyThat's the pattern the current
--no-passcodedocstring recommends, butit requires the operator to:
instance.
since kanban itself is now wide open.
For small self-hosted setups (one workstation behind a NAT, used by a
single developer or a 2–3-person team), a shared static passcode is much
closer to what the operator actually wants.
Environment
v24.16.0(nvm-managed)e.g.
kanban --host 192.168.1.29 --no-open --no-passcode)localhostand LAN-IP binding tested — the LAN binding is thecase that should respect
--no-passcodeand doesn't.Happy to send a draft PR for the bug fix and/or the
--passcodeoptionif helpful.