Skip to content

Forum App

Four packages and one auth bridge give you a working forum: channels, threads, replies, reactions, moderation, bans, reports, and realtime notifications.

Terminal window
bun add @lastshotlabs/slingshot @lastshotlabs/slingshot-auth @lastshotlabs/slingshot-community @lastshotlabs/slingshot-notifications @lastshotlabs/slingshot-permissions hono zod
app.config.ts
import type { Context, Next } from 'hono';
import { defineApp, getActor } from '@lastshotlabs/slingshot';
import { createAuthPlugin } from '@lastshotlabs/slingshot-auth';
import { createCommunityPackage } from '@lastshotlabs/slingshot-community';
import { createNotificationsPackage } from '@lastshotlabs/slingshot-notifications';
import { createPermissionsPackage } from '@lastshotlabs/slingshot-permissions';
const communityAuthBridge = async (c: Context, next: Next) => {
const actor = getActor(c);
if (actor.kind !== 'anonymous')
c.set('communityPrincipal', { subject: actor.id, roles: actor.roles ?? [] });
await next();
};
export default defineApp({
port: 3000,
security: { signing: { secret: process.env.SECRET! } },
middleware: [communityAuthBridge],
plugins: [
createAuthPlugin({
auth: { roles: ['user', 'moderator'], defaultRole: 'user' },
db: { auth: 'memory', sessions: 'memory', oauthState: 'memory' },
}),
],
packages: [
createNotificationsPackage({
dispatcher: { enabled: false, intervalMs: 30_000, maxPerTick: 500 },
}),
createPermissionsPackage(),
createCommunityPackage({
containerCreation: 'user',
}),
],
});

slingshot-community mounts all routes under /community:

ResourceRoutes
ContainersGET /community/containers, POST /community/containers, GET /community/containers/:id
ThreadsGET /community/containers/:id/threads, POST /community/containers/:id/threads, GET /community/threads/:id
RepliesGET /community/threads/:id/replies, POST /community/threads/:id/replies
ReactionsPOST /community/threads/:id/reactions, POST /community/replies/:id/reactions
ModerationPOST /community/threads/:id/lock, POST /community/threads/:id/pin
ReportsPOST /community/threads/:id/report, POST /community/replies/:id/report
BansPOST /community/bans

Community middleware reads communityPrincipal — not raw auth state. The bridge maps the authenticated actor and roles (set by slingshot-auth) into that shape. This keeps the packages independent: community doesn’t depend on auth internals, and auth doesn’t know about community.

containerCreation: 'user'; // Any authenticated user can create containers
containerCreation: 'admin'; // Only admins can create containers

Use 'admin' when containers are structured sections that only staff should manage.

slingshot-notifications mounts its own SSE inbox at /notifications/sse when the plugin is enabled.

Clients connect to GET /notifications/sse with the same auth they use for the rest of the app. Notification events arrive as SSE messages scoped to the authenticated user.

Use declarative AutoModRule records for pre-persist moderation instead of callback config. The community package persists reports, warnings, bans, and audit log entries as first-class entities.

Grant roles to users on specific containers:

await permissionsAdapter.createGrant({
subjectId: userId,
subjectType: 'user',
resourceType: 'community:container',
resourceId: containerId,
roles: ['moderator'],
effect: 'allow',
grantedBy: 'system',
});

Built-in community roles: owner (full access), moderator (pin, lock, ban), member (create threads and replies).