Skip to content
This repository was archived by the owner on Apr 13, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ docker compose -f deploy/docker/docker-compose.dev.yml down
docker volume rm docker_dev-postgres-data
```

## Troubleshooting

### I changed `INITIAL_ADMIN_EMAIL` or SSO and can’t sign in

If you need to re-bootstrap the initial admin account (for example after changing `INITIAL_ADMIN_EMAIL`), re-run the initializer:

```sh
npm run init
```

### Environment variables

Local development uses the same env variable names as Docker (for example, `AUTH_SECRET`, `AUTH_URL`, `DATABASE_URL`). The local `.env.example` also includes `TEST_DATABASE_URL` for the test runner.
Expand Down
10 changes: 10 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ docker exec rtap-web npm run seed:demo

- Server logs emit to stdout/stderr (structured JSON in production, pretty in development). Rely on Docker and the host OS for collection and rotation.
- Log level defaults: `debug` in development, `info` in production. Override with `LOG_LEVEL`.

## Troubleshooting

### I changed `INITIAL_ADMIN_EMAIL` or SSO and can’t sign in

The bootstrap admin account is created during initialization. If you need to re-bootstrap it (for example after changing `INITIAL_ADMIN_EMAIL`), re-run the init script inside the web container:

```sh
docker exec rtap-web npm run init
```
26 changes: 11 additions & 15 deletions src/server/init/ensure-initialized.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,17 @@ export function ensureInitialized(db: PrismaClient): Promise<void> {
if (initPromise) return initPromise;

initPromise = (async () => {
// 1) Ensure admin exists (if no users at all)
const userCount = await db.user.count();
if (userCount === 0) {
const defaultEmail = process.env.INITIAL_ADMIN_EMAIL?.trim().toLowerCase() ?? "admin@example.com";

await db.user.upsert({
where: { email: defaultEmail },
update: { role: UserRole.ADMIN },
create: {
email: defaultEmail,
name: "Admin User",
role: UserRole.ADMIN,
},
});
}
// 1) Ensure the bootstrap admin exists (every init, no auto-provisioning outside env)
const defaultEmail = process.env.INITIAL_ADMIN_EMAIL?.trim().toLowerCase() ?? "admin@example.com";
await db.user.upsert({
where: { email: defaultEmail },
update: { role: UserRole.ADMIN },
create: {
email: defaultEmail,
name: "Admin User",
Comment thread
initstring marked this conversation as resolved.
Outdated
role: UserRole.ADMIN,
},
});

// 2) Ensure MITRE data exists
const tacticCount = await db.mitreTactic.count();
Expand Down
4 changes: 2 additions & 2 deletions src/test/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ describe("ensureInitialized", () => {
delete process.env.INITIAL_ADMIN_EMAIL;
});

it("creates admin when no users and seeds MITRE when empty", async () => {
const user = { count: vi.fn().mockResolvedValue(0), upsert: vi.fn() };
it("creates admin and seeds MITRE when empty", async () => {
const user = { upsert: vi.fn() };
const mitreTactic = { count: vi.fn().mockResolvedValue(0), upsert: vi.fn() };
const mitreTechnique = { upsert: vi.fn() };
const mitreSubTechnique = { upsert: vi.fn() };
Expand Down
Loading