Skip to content

--no-passcode flag is silently ignored (commander/key-name mismatch), and a --passcode <value> option would be more useful for managed deployments #536

@phuoccm

Description

@phuoccm

Summary

Two related items:

  1. 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.
  2. 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:

  1. Stand up an extra TLS-terminating proxy in front of every kanban
    instance.
  2. Implement and configure its own auth (basic auth, OAuth, mTLS, etc.).
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions