public
anord
read
Ledger
Why work hard when you can work easier?
Languages
Repository composition by tracked source files.
TypeScript
86%
CSS
10%
SQL
3%
Shell
1%
HTML
0%
Commit
Remove demo seed support from setup flow
8d0ce83
.env.example | 1 -
README.md | 21 ++-------
apps/api/src/config/env.ts | 4 --
apps/api/src/db/seed.ts | 106 +--------------------------------------------
4 files changed, 4 insertions(+), 128 deletions(-)
Diff
diff --git a/.env.example b/.env.example
index 9543fa2..0b1a019 100644
--- a/.env.example
+++ b/.env.example
@@ -10,7 +10,6 @@ POSTGRES_PASSWORD=ledger
DATABASE_URL=postgresql://ledger:ledger@postgres:5432/ledger
REDIS_URL=redis://redis:6379
-LEDGER_ENABLE_DEMO_SEED=false
JWT_SECRET=change-me
SESSION_COOKIE_NAME=ledger_session
diff --git a/README.md b/README.md
index 4476c78..06a78a1 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ This repository currently ships a real MVP foundation:
## Fresh install behavior
-Ledger no longer assumes demo accounts on a normal install.
+Ledger starts with real setup, not demo accounts.
- `docker compose up` now runs a single `bootstrap` container that installs dependencies once into a shared Docker volume
- PostgreSQL and Redis stay internal to the Compose network by default
@@ -22,7 +22,7 @@ Ledger no longer assumes demo accounts on a normal install.
- the site name and brand color
- the footer text
- the initial public knowledge base toggle
-- Demo users and sample content are only created when `LEDGER_ENABLE_DEMO_SEED=true`
+- After setup, the instance is ready for real configuration, integrations, and content
## Monorepo layout
@@ -76,7 +76,7 @@ Services:
- Postgres: internal-only in Docker Compose
- Redis: internal-only in Docker Compose
-On first boot, open the frontend and complete the setup wizard instead of using a pre-seeded admin login.
+On first boot, open the frontend and complete the setup wizard.
## Without Docker
@@ -89,21 +89,6 @@ npm run dev:web
npm run dev:worker
```
-## Optional demo seed
-
-If you want the old demo-style experience with seeded users and pages:
-
-```bash
-LEDGER_ENABLE_DEMO_SEED=true
-```
-
-All demo users use password `Password123!`.
-
-- `[email protected]`
-- `[email protected]`
-- `[email protected]`
-- `[email protected]`
-
## Important endpoints
- `POST /api/auth/login`
diff --git a/apps/api/src/config/env.ts b/apps/api/src/config/env.ts
index 908fedf..d5b3f5e 100644
--- a/apps/api/src/config/env.ts
+++ b/apps/api/src/config/env.ts
@@ -7,10 +7,6 @@ const envSchema = z.object({
LEDGER_APP_URL: z.string().url().default("http://localhost:5173"),
DATABASE_URL: z.string().default("postgresql://ledger:ledger@localhost:5432/ledger"),
REDIS_URL: z.string().default("redis://localhost:6379"),
- LEDGER_ENABLE_DEMO_SEED: z
- .enum(["true", "false"])
- .default("false")
- .transform((value) => value === "true"),
JWT_SECRET: z.string().min(8).default("change-me"),
SESSION_COOKIE_NAME: z.string().default("ledger_session"),
PASSWORD_PEPPER: z.string().min(4).default("change-me"),
diff --git a/apps/api/src/db/seed.ts b/apps/api/src/db/seed.ts
index ddce7c7..5884d13 100644
--- a/apps/api/src/db/seed.ts
+++ b/apps/api/src/db/seed.ts
@@ -1,5 +1,3 @@
-import { env } from "../config/env.js";
-import { hashPassword } from "../services/auth.js";
import { pool } from "./pool.js";
async function main() {
@@ -47,109 +45,7 @@ async function main() {
`
);
- if (env.LEDGER_ENABLE_DEMO_SEED) {
- const roles = await pool.query(`SELECT id, key FROM roles`);
- const roleMap = new Map(roles.rows.map((row) => [row.key, row.id]));
-
- const users = [
- ["[email protected]", "Ledger Owner", "owner"],
- ["[email protected]", "Ledger Admin", "admin"],
- ["[email protected]", "Ledger Editor", "editor"],
- ["[email protected]", "Ledger Viewer", "viewer"]
- ];
-
- for (const [email, displayName, roleKey] of users) {
- await pool.query(
- `
- INSERT INTO users (email, password_hash, display_name, primary_role_id)
- VALUES ($1, $2, $3, $4)
- ON CONFLICT (email) DO NOTHING
- `,
- [email, await hashPassword("Password123!"), displayName, roleMap.get(roleKey)]
- );
- }
-
- const owner = await pool.query(`SELECT id FROM users WHERE email = '[email protected]'`);
- const editor = await pool.query(`SELECT id FROM users WHERE email = '[email protected]'`);
- const engineeringGroup = await pool.query(`SELECT id FROM groups WHERE name = 'engineering'`);
-
- await pool.query(
- `INSERT INTO group_memberships (user_id, group_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
- [editor.rows[0].id, engineeringGroup.rows[0].id]
- );
-
- await pool.query(
- `
- INSERT INTO spaces (name, key, description, visibility, created_by_user_id)
- VALUES
- ('Docs', 'docs', 'Public product documentation', 'public', $1),
- ('Ops', 'ops', 'Internal runbooks and procedures', 'internal', $1)
- ON CONFLICT (key) DO NOTHING
- `,
- [owner.rows[0].id]
- );
-
- const docsSpace = await pool.query(`SELECT id FROM spaces WHERE key = 'docs'`);
- const opsSpace = await pool.query(`SELECT id FROM spaces WHERE key = 'ops'`);
-
- const existingPublic = await pool.query(`SELECT id FROM pages WHERE slug = 'welcome-to-ledger'`);
- if (!existingPublic.rowCount) {
- const page = await pool.query(
- `
- INSERT INTO pages (space_id, title, slug, excerpt, visibility, state, is_public, owner_user_id)
- VALUES ($1, 'Welcome to Ledger', 'welcome-to-ledger', 'Ledger helps teams publish trusted answers.', 'public', 'published', true, $2)
- RETURNING id
- `,
- [docsSpace.rows[0].id, owner.rows[0].id]
- );
-
- const revision = await pool.query(
- `
- INSERT INTO page_revisions (page_id, title, slug, excerpt, visibility, state, body_markdown, edited_by_user_id)
- VALUES ($1, 'Welcome to Ledger', 'welcome-to-ledger', 'Ledger helps teams publish trusted answers.', 'public', 'published', '# Welcome to Ledger\n\nLedger is a secure knowledge base for public docs and internal answers.\n\n## What you can do\n\n- Publish public docs\n- Keep internal pages permission-aware\n- Search across trusted knowledge', $2)
- RETURNING id
- `,
- [page.rows[0].id, owner.rows[0].id]
- );
-
- await pool.query(`UPDATE pages SET current_revision_id = $2 WHERE id = $1`, [
- page.rows[0].id,
- revision.rows[0].id
- ]);
- }
-
- const existingInternal = await pool.query(`SELECT id FROM pages WHERE slug = 'incident-runbook'`);
- if (!existingInternal.rowCount) {
- const page = await pool.query(
- `
- INSERT INTO pages (space_id, title, slug, excerpt, visibility, state, is_public, owner_user_id)
- VALUES ($1, 'Incident Runbook', 'incident-runbook', 'Internal escalation workflow for incidents.', 'restricted', 'published', false, $2)
- RETURNING id
- `,
- [opsSpace.rows[0].id, owner.rows[0].id]
- );
-
- const revision = await pool.query(
- `
- INSERT INTO page_revisions (page_id, title, slug, excerpt, visibility, state, body_markdown, edited_by_user_id, tag_snapshot)
- VALUES ($1, 'Incident Runbook', 'incident-runbook', 'Internal escalation workflow for incidents.', 'restricted', 'published', '# Incident Runbook\n\nUse this page when triaging a Sev-1 incident.\n\n## First 10 minutes\n\n- Declare incident commander\n- Open incident channel\n- Update status page', $2, '["internal","ops"]'::jsonb)
- RETURNING id
- `,
- [page.rows[0].id, owner.rows[0].id]
- );
-
- await pool.query(`UPDATE pages SET current_revision_id = $2 WHERE id = $1`, [
- page.rows[0].id,
- revision.rows[0].id
- ]);
- await pool.query(`INSERT INTO page_permissions (page_id, group_id) VALUES ($1, $2)`, [
- page.rows[0].id,
- engineeringGroup.rows[0].id
- ]);
- }
- }
-
- console.log(env.LEDGER_ENABLE_DEMO_SEED ? "Seeded Ledger demo data" : "Seeded Ledger base data");
+ console.log("Seeded Ledger base data");
}
main()