Skip to main content

crewship admin

cmd/crewship/cmd_admin.go exposes the operator-on-the-host recovery surface. Every subcommand under admin opens the local SQLite database directly (no HTTP, no auth token) — the server doesn’t even need to be running. Use these when a user is locked out of the UI. The “credential” for these commands is shell access to the host. That matches what GitLab (gitlab-rake gitlab:password:reset), Gitea (gitea admin user change-password), Nextcloud (occ user:resetpassword), and Mattermost (mmctl user change-password) all do — if you can SSH to the box, you ARE the admin.
The credential for these commands is shell access to the host — there is no crewship login token. Requirements:
  • Read+write access to the data directory (default ~/.crewship). The binary opens ~/.crewship/crewship.db directly via openAdminDB.
  • The crewship binary must run on the same host as the data directory.
  • The host’s filesystem permissions are the authentication.

Subcommands


crewship admin reset-password

Rotate a user’s password.
The hashed value is written into users.hashed_password, the brute-force lockout state is cleared (failed_login_count=0, locked_until=NULL), and every active session row for that user is revoked as a side effect (the audit trail records revoked_reason='password_change').
Omit both --password and --password-stdin to get an interactive prompt with hidden input — the right default for ad-hoc recovery from a host shell, and the only form that never leaks to shell history or ps.

crewship admin list-users

Dump every row in the users table, ordered by created_at ASC. Output columns: EMAIL, NAME, CREATED (created_at), LOCKED (derived from locked_until), FAILS (failed_login_count), ROLES (role@workspace-slug, comma-joined across workspaces).
The lockout column reflects the users.locked_until timestamp set by the failed-login throttle — list-users --locked-only followed by reset-password is the canonical “unlock this user” pair.

crewship admin promote

Grant a workspace role to an existing user. Roles ascend VIEWER < MEMBER < MANAGER < ADMIN < OWNER; the command updates the user’s existing row in workspace_members (the user must already be a member — a non-member errors with user is not a member of workspace).
This is the post-init step for bootstrapping a second admin: the first user from crewship init is OWNER automatically; everyone else starts as a regular member and needs admin promote to gain elevated access without going through the UI.

crewship admin invalidate-sessions

Force-logout every active session for one user without changing their password. The user can still sign in normally afterwards — they just have to re-authenticate on every device they were already on.
Each revoked row is stamped revoked_reason='admin_invalidate' so the audit trail distinguishes this from the side-effect revokes that fire during reset-password (those carry revoked_reason='password_change').
  • Laptop stolen / recovered, the password is believed safe but a cached cookie might still be on the device.
  • Suspected token leak via Slack screenshot, browser history dump, etc.
  • Periodic compliance sweep — “log everyone out of yesterday’s sessions”.

crewship admin sessions list

Forensic read of user_sessions for one user — mirrors crewship session list but for arbitrary users (the user-side command is self-only). Admin-only via direct DB access.
Output columns: ID, STATUS (derived from revoked_at + expires_at; revoked rows show revoked:<reason>), CREATED, LAST USED (last_used_at), EXPIRES, IP, UA (user_agent). Sorted created_at DESC. A typo’d email reports no user with email <addr> rather than silently returning zero rows. Pair with admin invalidate-sessions to act on what sessions list surfaces.

crewship admin prune-legacy

Removes orphaned pre-C1 (slug-only) crew docker resources — volumes and containers named <prefix>-{home,tools,team}-<slug> (e.g. crewship-3-tools-engineering) left over from before the C1 naming change (2026-06 audit, which re-keyed crew resources to include the crew id).
Unlike the rest of crewship admin, this command is HTTP-backed. The docker daemon lives behind the running server, not the local SQLite DB, so this command needs a reachable server and an authenticated session (crewship login) — it is not a host-only recovery command. It is filed here because it is an operator/admin maintenance action.

Why this exists

A legacy slug-only volume survives crewship seed --nuke: the nuke clears the database, and crew teardown removes the id-scoped volumes — but never the orphaned slug-only ones, because no crew row references them. While they exist, the runtime’s legacy-resource guard refuses to start the crew’s container, and the failure reaches users only as a generic “failed to start agent container”. Every agent in the affected crew fails; a seed --smoke-test reports N/N agents failed. crewship doctor surfaces this proactively: the legacy crew resources check calls the authenticated GET /api/v1/admin/legacy-resources endpoint and WARNs with this command as the remediation when orphaned resources are present. (Detection runs on this admin endpoint, not on the unauthenticated /healthz hot path, so a slow docker daemon can never stall health probes.) The prune is instance-wide: legacy docker names carry no workspace or crew id, so detection (what crewship doctor surfaces) and prune enumerate the same full crew set — otherwise the doctor could WARN on a slug the prune can never reach. It removes only the orphaned legacy names; the id-scoped resources the live runtime uses are excluded (slug/id-collision safe) and never touched. It reports each resource removed:
Requires the OWNER or ADMIN role. Returns 503 when the server’s container provider is not docker (nothing to prune).

See also

  • crewship init — first-user bootstrap on a fresh database. After init, admin promote is how you add a second OWNER.
  • crewship doctor — the legacy crew resources check WARNs when a prune is needed.
  • crewship session — user-scoped self-service of the same user_sessions table.
  • crewship doctor — adjacent host-side diagnostics (data dir, schema version, container runtime).