Framework Guide¶
This page distills the conventions that every challenge server should follow.
Use it as a checklist when bootstrapping a new challenges/<slug>/server app.
1. Bootstrap Observability¶
from ctf.common import configure_logging_and_metrics
metrics = configure_logging_and_metrics(ctf_slug="arkiv-beacon", service="server")
metrics.record_server_online(addr="0.0.0.0:8080")
- Log lines should follow the readable logfmt-ish style described in the repo guidelines. Keep sensitive data out of logs.
- The returned
Metricshelper exposes convenience counters and histograms that can be safely called even when Prometheus is disabled.
2. Handle Arkiv Metadata Edge Cases¶
from ctf.common import get_entity_metadata_safe
metadata = await get_entity_metadata_safe(client, entity_key)
Use the helper when you need entity metadata during gameplay. It falls back to the raw RPC call if the higher-level SDK method temporarily fails so you can keep servicing requests without special retry logic.
3. React to Events, Don’t Poll¶
- Use
ArkivClient.watch_logs(...)(from the SDK) to subscribe to entity events. - Convert annotations to dictionaries with
anns_to_dictto make filtering easier. - Never spin background loops-everything should be driven by WebSocket events.
4. Celebrate Completions¶
from ctf.common import asyncio_run_safely, send_discord_webhook
asyncio_run_safely(send_discord_webhook(challenge, team, winner_address))
- The helper reads the webhook URL from
DISCORD_WEBHOOK_URLso environments without a webhook stay silent. - Always run the call in the background; we never block the event loop on network I/O.
5. Document as You Go¶
When you add new helpers to ctf/common, include rich docstrings describing
expected usage patterns. The API reference is generated at build time, so the
documentation automatically stays current.