Knowledge Basket is a SvelteKit product that brings together five public "coils":
- Events
- Funding
- Red Pages
- Jobs
- Toolbox
This is an in-progress product, not a greenfield starter. The codebase already includes meaningful public and admin implementation work, but only Events is currently close to full end-to-end operations. Funding, Red Pages, Jobs, and Toolbox should be treated as curated beta surfaces until shared submission, moderation, and admin tooling is completed.
The code is canonical.
- Product behavior:
src/routes/** - Shared UI and interaction patterns:
src/lib/components/** - Server/data behavior:
src/lib/server/** - Theme tokens:
src/lib/theme/theme.css
Docs in docs/ are there to explain the current implementation and intended direction, but if a doc conflicts with route or server code, trust the code first and update the doc.
Use docs/README.md as the map of where product, review, and planning docs currently live.
- Public surfaces exist for all five coils.
- Public organization and venue pages exist.
- Admin now has stronger operational tooling for Events, Sources, Organizations, Venues, and Search.
- Source review, source health, candidate publishing, source-to-live merge history, and search reindexing now exist under
/admin/sourcesand/admin/settings/search. - Organization and venue admin now support aliases, duplicate suggestions, and merge workflows.
- Public
/eventsnow reads reviewed database content instead of merging a hardcoded runtime iCal feed. - Public submissions are live for Events, Funding, Red Pages, Jobs, and Toolbox. Each submit flow writes a pending record for moderation.
- Global search is available, but multi-coil results depend on Meilisearch indexing. Without Meilisearch, fallback search is events-only.
From the site/ directory:
pnpm install
cp .env.local.example .env
pnpm devOpen the app at http://localhost:5173.
From the repo root, start supporting services:
docker compose up -dCommon local dependencies:
- Postgres
- Meilisearch
- MinIO
- Redis
If you only need search locally, you can start just Meilisearch:
cd ..
docker compose up -d meilisearch
curl http://localhost:7700/healthUse .env.local.example as the local development template and copy it to site/.env.
Use .env.example as the production or staging checklist for your hosting environment variables.
Public placeholder/branding assets are now expected to already exist in MinIO, with
PUBLIC_ASSET_BASE_URL pointing at a public bucket root or CDN origin. pnpm images:sync
is a one-time migration tool for a checkout that still has the legacy static/images files;
steady-state environments should restore the bucket from snapshots or replication instead.
Useful scripts:
pnpm db:push
pnpm db:generate
pnpm db:migrate
pnpm db:studio
pnpm db:seed
pnpm db:seed:events
pnpm db:seed:coils
pnpm db:seed:sources
pnpm auth:schemaNotes:
pnpm db:seednow runs the launch-data seed: events, non-event coils, and source registry seeds when the shared source seed file is available.pnpm db:seed:eventsruns the legacy events CSV seed only.pnpm db:seed:coilsseeds Funding, Red Pages, Jobs, and Toolbox sample content.pnpm db:seed:sourcesseeds the source registry from sharedseed-sources.jsondata when that file is present in the linked data repo.- Search indexing uses Meilisearch when configured.
- Health status for DB, search, object storage, and source ops is exposed at
GET /api/health. - After applying DB migrations, restart the dev server so cached schema-health warnings clear.
- Recent additive migrations include source-ops snapshot support and aliases on organizations/venues.
POST /api/reindexis protected in production. Use an admin/moderator session or sendx-reindex-secretmatchingREINDEX_SECRET.- The admin UI also exposes search reindexing at
/admin/settings/search. sitemap.xml,robots.txt, andmanifest.webmanifestare generated as part of the app surface.pnpm startnow runspnpm db:migratebefore booting the Node server so deploys apply schema changes as part of startup on single-instance environments.
Launch environments should set, at minimum:
- Core runtime:
DATABASE_URL,BETTER_AUTH_SECRET, and a public origin (ORIGIN, orRAILWAY_PUBLIC_DOMAINwhile bootstrapping on Railway) - Email/auth:
SMTP_HOST,SMTP_PORT,SMTP_FROM, plusSMTP_SECUREorSMTP_REQUIRE_TLS - Search:
MEILISEARCH_HOST,MEILISEARCH_API_KEY - Object storage:
MINIO_ENDPOINT,MINIO_ACCESS_KEY,MINIO_SECRET_KEY,MINIO_BUCKET - Public assets:
PUBLIC_ASSET_BASE_URL - Privileged ops:
REINDEX_SECRET,SOURCE_OPS_SECRET
Recommended observability settings:
SENTRY_DSNand/orPUBLIC_SENTRY_DSNPUBLIC_POSTHOG_KEYand optionalPUBLIC_POSTHOG_HOSTLOG_LEVELERROR_WEBHOOK_URL
In production, startup now validates this contract and fails fast on missing or clearly invalid required settings.
The app is already configured for Railway's Node deployment path:
- SvelteKit uses
@sveltejs/adapter-node railway.tomlsets the build command, runspnpm db:migrateas a pre-deploy step, starts the app withpnpm start:app, and health-checksGET /api/healthpnpm startis still available for non-Railway single-instance environments that want migrations on boot
Recommended Railway setup:
- Create an app service from this
site/directory. - Add a Postgres service and point
DATABASE_URLat it. - Generate a Railway public domain.
- Set
ORIGINto your app URL. If you are using only the generated Railway domain, the app can fall back toRAILWAY_PUBLIC_DOMAIN, but explicitORIGINis still the safer default. - Add the rest of the required production variables from the contract above.
- Redeploy and verify
GET /api/health, auth flows, and/sitemap.xml.
Operational notes:
- Railway injects
PORT; the Node adapter will bind to it automatically. - The generated
RAILWAY_PUBLIC_DOMAINis good enough for a first deploy, but switchORIGINto your custom domain before finalizing Google OAuth or canonical URLs. - If you later split this repo into multiple Railway services, move the start command into each service's dashboard settings so the shared
railway.tomldoes not force the same process everywhere.
As of the current takeover baseline:
pnpm checkshould pass.pnpm lintshould pass.pnpm testruns the smoke and handler tests.pnpm test:search:indexedvalidates the Meilisearch-backed contract.pnpm test:search:degradedvalidates compatibility-mode search when Meilisearch is unavailable.pnpm test:e2eruns browser coverage plus axe checks for representative public, auth, account, and admin flows.pnpm buildshould pass and now targets the Node adapter.
docs/README.mddocs/TAKEOVER_AUDIT.mddocs/ADR-001-source-of-truth.mddocs/ADR-002-curated-beta-coils.mddocs/DESIGN_SYSTEM.mddocs/PERFORMANCE.mddocs/ANALYTICS_AND_MARKETING.mddocs/PRODUCTION_RUNBOOK.mddocs/ops-content-workflows.mddocs/SOURCE_OPS_HANDOFF.md
The current implementation direction is:
- Stabilize the existing product.
- Preserve the public left-rail browse/filter layout and improve its ergonomics, responsiveness, and product polish.
- Keep shadcn-svelte as the primary UI foundation, but preserve the current public header look and feel rather than redesigning it while swapping primitives underneath.
- Standardize public/admin patterns instead of rewriting from scratch.
- Build out non-event moderation and admin tooling to match the now-live public submission flows.
- Desktop main navigation should keep the current KB header styling and information density.
- Under the hood, desktop primary nav can use shadcn-svelte
navigation-menupatterns, but it should not read like a visual redesign. - Mobile main navigation should use a sidebar-style overlay, not a filter drawer or generic sheet pattern.
- Public browse filters should stay in the left rail on desktop.
- On mobile, browse search should stay exposed in-page while deeper result filters open from a bottom drawer.