docs: update documentation to OKF v0.1 format

This commit is contained in:
OpenVelo Agent
2026-07-23 02:24:40 +00:00
parent cbfa9c35ca
commit ceebaa06aa
5 changed files with 159 additions and 2 deletions
+5
View File
@@ -85,6 +85,11 @@ column are sorted by `difficulty` (`LOW < MEDIUM < HIGH`) then by name.
Only `enabled = 1` rows are returned. The query joins Only `enabled = 1` rows are returned. The query joins
`challenge` and `category` once and groups in memory. `challenge` and `category` once and groups in memory.
On a fresh database the `SeedSampleChallenges1700000000600`
migration populates the board with a representative sample set
spanning every canonical system category so the page renders cards
without any manual admin action.
# `GET /api/v1/challenges/status` # `GET /api/v1/challenges/status`
Authenticated event-window snapshot (the same `EventStatePayload` Authenticated event-window snapshot (the same `EventStatePayload`
+8
View File
@@ -56,6 +56,14 @@ canonical enums, the `enabled` flag, `updated_at`, the
category FK stays at `ON DELETE RESTRICT` so the category FK stays at `ON DELETE RESTRICT` so the
`CATEGORY_HAS_CHALLENGES` business rule is preserved. `CATEGORY_HAS_CHALLENGES` business rule is preserved.
The `SeedSampleChallenges1700000000600` migration inserts a small
representative set of sample challenges (one or more per canonical
system category) on a fresh database so the `/challenges` board has
cards immediately after bootstrap. It is idempotent: if the
`challenge` table already contains rows (e.g. an admin imported a
custom challenge set), it does nothing. Sample flags follow the
`flag{<token>}` convention and are exposed only inside admin views.
## `challenge_file` ## `challenge_file`
| Column | Type | Description | | Column | Type | Description |
@@ -0,0 +1,133 @@
---
type: database
title: Seed Sample Challenges Migration (1700000000600)
description: Forward-only migration that inserts a representative sample set of challenges on a fresh database so the /challenges board renders cards without manual admin action.
tags: [database, migration, challenge, seed, sample]
timestamp: 2026-07-23T02:23:34Z
---
# Purpose
A brand-new HIPCTF database (default path
[`./data/db.sqlite`](/database/schema.md)) has no `challenge` rows until
an admin imports or creates them. The
`SeedSampleChallenges1700000000600` migration bridges that gap by
inserting a small, representative sample set so the authenticated
`/challenges` board (see [Challenges Board](/guides/challenges-board.md))
already has cards on the very first visit and the public landing can
demonstrate the platform with real content.
The migration runs at the end of the `MIGRATIONS` array in
[`DatabaseModule`](/architecture/backend-modules.md) so every other
category / challenge schema migration has already landed and the
`category` rows it joins against are present.
# File
`backend/src/database/migrations/1700000000600-SeedSampleChallenges.ts`
Registered last in `backend/src/database/database.module.ts` so it
runs after `UpgradeChallengeAdminSchema1700000000500`.
# Sample set
Eight challenges, one or more per canonical system category. Source
of truth is the typed `SAMPLE_CHALLENGES` array exported by the
migration:
| Name | `system_key` | Difficulty | `initial_points` | `minimum_points` | `decay_solves` | Flag |
|-------------------|--------------|------------|------------------|------------------|----------------|--------------|
| Alpha Cipher | `CRY` | `LOW` | 200 | 100 | 5 | `flag{alpha}` |
| Beta Cipher | `CRY` | `MEDIUM` | 300 | 100 | 10 | `flag{beta}` |
| Zeta Key | `CRY` | `HIGH` | 500 | 100 | 10 | `flag{zeta}` |
| Web Welcome | `WEB` | `LOW` | 100 | 50 | 10 | `flag{web1}` |
| Mobile Mayhem | `PWN` | `MEDIUM` | 300 | 100 | 10 | `flag{mob1}` |
| Hardware Hello | `HW` | `LOW` | 100 | 50 | 10 | `flag{hw1}` |
| Markdown Mystery | `MSC` | `LOW` | 100 | 50 | 10 | `flag{md}` |
| Reverse Ranger | `REV` | `MEDIUM` | 300 | 100 | 10 | `flag{rev1}` |
All rows are inserted with `protocol='WEB'`, `port=NULL`,
`ip_address=''`, and `enabled=1` so they appear on the board the
moment the event window opens (see [Event Window](/guides/event-window.md)).
Categories are resolved by `system_key` rather than UUID so the seed
works regardless of the UUIDs the category migration assigned at
install time. Sample flags follow the `flag{<token>}` convention and
are exposed only inside admin views — the public challenge DTO
continues to strip them (see [Challenges Endpoints](/api/challenges.md)).
# What the `up()` does
1. **Idempotency guard**`SELECT COUNT(*) FROM "challenge"`. If the
table already contains rows the migration returns immediately; an
admin that imported a custom challenge set first is never
overwritten.
2. **System-category lookup**
`SELECT id, system_key FROM "category" WHERE system_key IS NOT NULL`
and builds an in-memory `Map<system_key, id>`. If fewer than six
canonical keys are present (the seed cannot bind) the migration
aborts as a no-op rather than inserting challenges into the wrong
category.
3. **Insert sample rows** — for each entry in `SAMPLE_CHALLENGES`, an
`INSERT INTO "challenge"` with a fresh UUID v4, ISO 8601
`created_at` / `updated_at` via `strftime('%Y-%m-%dT%H:%M:%fZ','now')`,
and `enabled=1`. Categories without a matching `system_key` are
silently skipped (defensive — only happens if the canonical six
are missing).
# Idempotency
* Running the migration twice (or restarting after it has been
recorded) leaves the same eight rows. The early-return count
guard skips the second run.
* Re-running the migration after a custom `challenge` row exists also
no-ops, so manually-imported content from the
[Admin — Challenges Full-Replace Import](/guides/admin-challenges-import.md)
flow is preserved.
# `down()`
Scoped to the seeded set: `DELETE FROM "challenge" WHERE "name" IN (...)`
using the eight fixed names from `SAMPLE_CHALLENGES`. It deliberately
matches **only** the sample names so unrelated admin rows are not
removed.
# Tests
| File | What it asserts |
|---|---|
| `tests/backend/migrations.spec.ts`*"seeds sample challenges on a fresh database (Job 906)"* | After all migrations run on an in-memory SQLite DB, the eight expected sample names are present. |
| `tests/backend/migrations.spec.ts`*"seeded sample challenges are enabled, schema-compatible, and point at the right categories (Job 906)"* | Every seeded row has `enabled=true`, a valid `difficulty`, `initial_points >= minimum_points`, a non-empty `flag`, and a `category_id` that resolves to one of the six canonical system categories. |
| `tests/backend/migrations.spec.ts`*"SeedSampleChallenges down() scope (Job 906)"* | `down()` removes only rows whose name is in the seed list and leaves manually-inserted rows of similar names (e.g. `Other`) intact. |
| `tests/backend/challenges-board.spec.ts` | Board-listing test now allows extra seeded cards inside the CRY column (`Alpha Cipher`, `Beta Cipher`, `Zeta Key`) while still asserting that the hand-inserted `alphacipher` LOW and `Zeta-key` HIGH rows occupy the bookended slots and that the disabled `hidden` row is excluded. |
# Operational notes
* On a fresh database the seed fires before the first app boot, so
[`GET /api/v1/challenges/board`](/api/challenges.md) returns the
sample columns immediately and the player-facing [Challenges Board](/guides/challenges-board.md)
shows eight cards. An admin deleting every challenge is the only
way to reach an empty board.
* To confirm on the command line after `npm run setup`:
```
sqlite3 ./data/db.sqlite 'SELECT COUNT(*) FROM challenge;'
```
Expected count: `8`. Re-running setup on a populated DB leaves the
count unchanged (idempotent guard).
* Played flags are usable for end-to-end demos: e.g. submitting
`flag{alpha}` against the Alpha Cipher card immediately awards
points and broadcasts the solve on the
[Scoreboard Stream](/guides/scoreboard-stream.md).
# See also
* [Database Schema Overview](/database/schema.md) — full migration list
and bootstrap behavior.
* [Challenge Tables](/database/challenges.md) — `challenge` schema,
indexes, and provenance.
* [Challenges Endpoints](/api/challenges.md) — board query and flag
submission.
* [Challenges Board](/guides/challenges-board.md) — user-facing guide.
* [Admin — Challenges](/guides/admin-challenges.md) — how an admin
replaces or extends the sample set.
* [Category Repair Migration](/database/category-repair-migration.md)
— earlier canonical-six migration this seed depends on.
+8 -1
View File
@@ -10,7 +10,14 @@ timestamp: 2026-07-23T00:10:00Z
`/challenges` is rendered by `ChallengesPage` `/challenges` is rendered by `ChallengesPage`
(`frontend/src/app/features/challenges/challenges.page.ts`) inside the (`frontend/src/app/features/challenges/challenges.page.ts`) inside the
authenticated shell. The page is gated by: authenticated shell.
On a fresh database the `SeedSampleChallenges1700000000600`
migration loads a representative sample set of challenges so the
board has cards the moment an admin configures the event window; an
empty board is only possible when the admin deleted every challenge.
The page is gated by:
| Gate | Where | | Gate | Where |
|---------------------------------------|--------------------------------------------------| |---------------------------------------|--------------------------------------------------|
+5 -1
View File
@@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin
controls. controls.
The docs below are organized by purpose so agents can pull just the slice The docs below are organized by purpose so agents can pull just the slice
they need. Last regenerated 2026-07-23T00:10:00Z. they need. Last regenerated 2026-07-23T02:23:34Z.
# Architecture # Architecture
@@ -35,6 +35,10 @@ they need. Last regenerated 2026-07-23T00:10:00Z.
Forward-only migration that adds `created_at`/`updated_at` to legacy Forward-only migration that adds `created_at`/`updated_at` to legacy
`category` tables and reconciles system rows to the canonical `category` tables and reconciles system rows to the canonical
CRY/HW/MSC/PWN/REV/WEB set. CRY/HW/MSC/PWN/REV/WEB set.
* [Seed Sample Challenges Migration](/database/seed-sample-challenges-migration.md) -
Forward-only migration that inserts a representative sample set of
challenges on a fresh database so the `/challenges` board has cards
immediately, with idempotency guards and scoped `down()`.
* [Auth and Settings Tables](/database/auth-settings.md) - `refresh_token` * [Auth and Settings Tables](/database/auth-settings.md) - `refresh_token`
and `setting` tables. and `setting` tables.
* [Blog Post Table](/database/blog-posts.md) - `blog_post` table. * [Blog Post Table](/database/blog-posts.md) - `blog_post` table.