6.3 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| api | System Endpoints | Bootstrap payload, public event status, public/authenticated SSE streams, and public event-window timestamps; the public `event/stream` also forwards `general` topic frames carrying `registrationsEnabled`. |
|
2026-07-22T16:15:00Z |
Endpoints
| Method | Path | Auth | Source |
|---|---|---|---|
GET |
/api/v1/bootstrap |
Public | backend/src/modules/system/system.controller.ts |
GET |
/api/v1/event/status |
Public | Same. |
GET |
/api/v1/event/stream |
Public (SSE) | Same. |
GET |
/api/v1/settings/event |
Public | Same. |
GET |
/api/v1/events/status |
Authenticated (SSE) | Same. |
GET |
/api/v1/scoreboard/stream |
Public (SSE) | Same. |
Path note: the authenticated status stream is mounted at the plural
/api/v1/events/statusso it can be distinguished from the legacy public/api/v1/event/statussnapshot endpoint.
GET /api/v1/bootstrap
Built by SystemService.bootstrap(). Public payload consumed by the
SPA's BootstrapService:
{
"initialized": true,
"pageTitle": "HIPCTF",
"logo": "",
"welcomeMarkdown": "# Welcome\n\n...",
"theme": { "id": "classic", "tokens": { "...": "..." } },
"defaultChallengeIp": "127.0.0.1",
"registrationsEnabled": false,
"passwordPolicy": {
"minLength": 12,
"requireMixed": true,
"description": "At least 12 characters with upper, lower, digit and symbol"
}
}
initializedistrueiff at least one admin user exists.themeis the resolvedTheme(one of the 10 canonical themes; see Theming).registrationsEnabledis read from thesettingtable (SETTINGS_KEYS.REGISTRATIONS_ENABLED) and gates the new public register endpoint (see Auth Endpoints).passwordPolicyis derived fromPASSWORD_MIN_LENGTH/PASSWORD_REQUIRE_MIXEDenv vars (see Change Password Guide).- The frontend
BootstrapServicecaches the initial bootstrap request throughload()/ready(). Itsrefresh()method fetches the payload again, updates signals, and reapplies the theme;/admin/generalcalls it after a successful settings update.
GET /api/v1/event/status (legacy public snapshot)
Returns the current event window computed by EventStatusService.getStatus():
{ "state": "Running", "countdownMs": 12345, "startUtc": "...", "endUtc": "...", "serverNowUtc": "..." }
status is Stopped while now < eventStartUtc (countdown), Running
between start and end, and Stopped once now >= eventEndUtc. This
endpoint is preserved for backward compatibility — new consumers should
use getState() (see below).
GET /api/v1/settings/event (public timestamps)
Returns the raw UTC event-window timestamps without computing state. Useful for the SPA when only the window endpoints are needed:
{ "eventStartUtc": "2026-07-21T12:00:00.000Z", "eventEndUtc": "2026-07-28T12:00:00.000Z" }
A null value for either field means that the underlying setting row
is missing or empty; in that case EventStatusService.getState() returns
the unconfigured state.
GET /api/v1/events/status (authenticated SSE)
Authenticated Server-Sent Events stream that pushes the full
EventStatePayload whenever it changes. Protected by the global
JwtAuthGuard — unauthenticated clients receive 401 before any frame
is sent.
Each frame:
{
"state": "countdown" | "running" | "stopped" | "unconfigured",
"serverNowUtc": "2026-07-21T12:00:00.000Z",
"eventStartUtc": "2026-07-21T12:00:00.000Z" | null,
"eventEndUtc": "2026-07-28T12:00:00.000Z" | null,
"secondsToStart": 3600,
"secondsToEnd": 604800
}
statetransitions:unconfigured—eventStartUtc/eventEndUtcsettings are missing or unparseable.countdown—now < eventStartUtc.running—start <= now <= end.stopped—now > eventEndUtc.
- The stream emits:
- An initial frame on connect (
defer(getState())). - A 60-second tick frame so the SPA can update even if no admin cron publishes through the hub.
- Any frame pushed via
SseHubService.publish().
- Consecutive identical payloads are suppressed via
distinctUntilChanged(JSON.stringify).
- An initial frame on connect (
See backend/src/common/services/event-status.service.ts for the
authoritative state machine; see
Event Window Guide for the full diagram and
the SPA wiring. The authenticated browser consumer uses the fetch-based
transport in frontend/src/app/core/services/authenticated-event-source.service.ts
so it can send the JWT Authorization header; native EventSource cannot set
that header.
GET /api/v1/event/stream (public SSE, legacy)
Public, flattened event stream that emits the legacy payload shape
(status, countdownMs, startUtc, endUtc, serverNowUtc) on a
1-second tick and on hub pushes. Preserved for backward compatibility
with the original landing page status badge.
admin/general updates are also broadcast on this stream as
{ topic: 'general', themeKey, registrationsEnabled } so the public
landing page can refresh its cached bootstrap whenever an admin toggles
the registrations flag (or any other general setting) without forcing
the user to hard-refresh. The frame is interleaved with the legacy
event-status frames. The legacy event-status frames are still emitted
on the 1-second tick so existing consumers are unaffected.
GET /api/v1/scoreboard/stream
Public SSE stream that pushes a flattened solve payload whenever a new
solve is published (backend/src/common/services/sse-hub.service.ts).