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.dbdirectly viaopenAdminDB. - The
crewshipbinary 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.
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').
When to reach for this instead of reset-password
When to reach for this instead of reset-password
- 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).
Why this exists
A legacy slug-only volume survivescrewship 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:
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. Afterinit,admin promoteis how you add a second OWNER.crewship doctor— thelegacy crew resourcescheck WARNs when a prune is needed.crewship session— user-scoped self-service of the sameuser_sessionstable.crewship doctor— adjacent host-side diagnostics (data dir, schema version, container runtime).