Overview
Human-owned documentation. This package should stay small and explicit about what it delegates.
Purpose
Section titled “Purpose”@lastshotlabs/slingshot-admin provides the admin HTTP surface for Slingshot. It is the package that
turns admin access checks, managed-user operations, permissions state, and optional mail rendering
into mounted routes.
The package does not own user storage, session storage, grant persistence, or identity policy. It depends on providers for all of that and should remain a thin route-and-guard assembly layer.
Design Constraints
Section titled “Design Constraints”createAdminPlugin()must remain provider-driven. It should not quietly reach into unrelated packages to discover auth or permissions implementations.- The root-level
createSlingshotAdminPlugin()is the place where Slingshot-specific convenience wiring happens. That wrapper intentionally defers route registration tosetupPostso it can read permissions from shared plugin state after other plugins have published it. - Admin route protection should stay centralized at the mount path. Today the package uses a single guard across admin, permissions, and mail routes so sub-routers cannot drift into inconsistent auth behavior.
Wiring providers
Section titled “Wiring providers”The admin plugin is a thin wrapper that coordinates provider objects you supply. Build the
providers in app.config.ts and pass them to createAdminPlugin():
// @skip-typecheckimport { defineApp } from '@lastshotlabs/slingshot';import { createAdminPlugin } from '@lastshotlabs/slingshot-admin';import { createMemoryAuditLog, createSlingshotAuthAccessProvider, createSlingshotAuthManagedUserProvider,} from '@lastshotlabs/slingshot-admin/providers';import { createAuthPlugin } from '@lastshotlabs/slingshot-auth';import { createPermissionsPackage } from '@lastshotlabs/slingshot-permissions';
export default defineApp({ plugins: [ createAuthPlugin({ auth: { roles: ['user', 'super-admin'], defaultRole: 'user' }, db: { auth: 'memory', sessions: 'memory', oauthState: 'memory' }, }), createAdminPlugin({ accessProvider: createSlingshotAuthAccessProvider(), managedUserProvider: createSlingshotAuthManagedUserProvider(), auditLog: createMemoryAuditLog(), // permissions can be omitted — the plugin will read it from // slingshot-permissions state at runtime. }), ], packages: [createPermissionsPackage()],});accessProvider only grants admin access to users with the super-admin role. auditLog
is an in-memory development implementation — entries are lost on process restart.
Operational Notes
Section titled “Operational Notes”- If admin boots without permissions, check whether you are using
createAdminPlugin()directly or the Slingshot root wrapper. Direct usage requires an explicitpermissionsobject. - If the Slingshot root wrapper throws about missing
PERMISSIONS_STATE_KEY, another plugin was expected to publish permissions state and did not. That is a wiring problem, not an admin-router problem. mailRendereris optional. When it is absent, mail preview routes should not be mounted.- Mail routes require
readpermission onadmin:mail. The mount-path auth guard alone is not sufficient for template enumeration or preview.
Gotchas
Section titled “Gotchas”- The admin package exports
./testinghelpers because the route layer is easiest to test with in-memory access and managed-user providers. - This package can look “too small” on first read. That is intentional. If business logic starts piling up here, it probably belongs in the backing providers or in a different package.
mountPathmust start with/; trailing slashes are trimmed before routes are mounted.- Mount path defaults to
/admin, but the mounted permissions sub-router lives under${mountPath}/permissions, so proxy and routing assumptions should be documented carefully in consumer apps.
Key Files
Section titled “Key Files”src/plugin.tssrc/routes/admin.tssrc/routes/permissions.tssrc/testing.ts