4.2 KiB
Didactyl — Agent Instructions
Build
Always use ./build_static.sh to build the binary. Never run make directly — the default all target prints a warning and exits with an error. make is kept only for auxiliary targets (make clean, make deps, make test_pool).
./build_static.sh # Release build (static MUSL via Docker)
./build_static.sh --debug # Debug build
./build_static.sh --platform=linux/arm64 # Cross-compile
Project Overview
Didactyl is a Nostr agent written in C (C99). It runs as a long-lived daemon that listens for DMs from its admin, processes them via an LLM, and can execute tools (post notes, query relays, manage skills, etc.).
Key Files
| File | Purpose |
|---|---|
src/main.c |
Entry point, CLI arg parsing, signer construction |
src/setup_wizard.c |
Interactive setup wizard (8 steps) |
src/config.c / src/config.h |
Config loading from genesis.jsonc |
src/nostr_handler.c / src/nostr_handler.h |
Nostr relay pool, event publish/subscribe |
src/llm.c / src/llm.h |
LLM HTTP client (OpenAI-compatible API) |
src/agent.c |
Agent loop: DM → LLM → tool dispatch → response |
src/tools/ |
Tool implementations (one file per tool) |
src/tools/tools_schema.c |
Tool schemas (JSON Schema for each tool) |
src/tools/tools_dispatch.c |
Tool dispatch routing |
src/default_events.h |
Default relays, skill templates |
src/signer_health.c / src/signer_health.h |
Signer health monitoring |
nostr_core_lib/ |
Vendored nostr_core_lib (v0.6.15) |
genesis.jsonc.example |
Example genesis config |
build_static.sh |
Static build script (Docker + Alpine + musl) |
Dockerfile.alpine-musl |
Dockerfile for static builds |
increment_and_push.sh |
Version bump + git tag + push |
Coding Conventions
- Language: C99 (
-std=c99) - Style: 4-space indentation, K&R braces
- Naming:
snake_casefor functions and variables,UPPER_CASEfor macros - Headers: Include guard via
#ifndef/#define/#endif - Error handling: Return negative error codes, use
NOSTR_SUCCESS(0) fromnostr_core_lib/nostr_core/nostr_common.h - Logging: Use
DEBUG_INFO(),DEBUG_WARN(),DEBUG_ERROR()macros fromsrc/debug.h - Memory: Manual malloc/free; no GC
- JSON: Use cJSON library (
nostr_core_lib/cjson/)
Key Architecture Decisions
- Signer abstraction: All crypto operations go through
nostr_signer_t(local or remote via n_signer). The signer is constructed inmain.cand passed through the tool context. - Role paths: Uses BIP-44 derivation paths (
role_path) instead of the oldnostr_indexselectors. Seedocs/SIGNER.md. - Wizard: The setup wizard (
src/setup_wizard.c) is a plain-text interactive TUI (no ncurses). It usesfprintf(stderr, ...)for output andfgets()for input. - Config persistence: Config is published as NIP-44 encrypted kind 30078 events on Nostr, keyed by
d_tag(e.g."user-settings"). - Skills: Agent skills are kind 31124 events with
d_tagidentifiers. They contain markdown with template variables like{{my_kind0_profile}},{{my_npub}},{{nostr_dm_history(...)}}.
Testing
cd tests && python3 run_tests.py # Run test suite
Tests are in tests/suites/ using a Python harness. Each test spawns a didactyl instance with a test genesis config and checks behavior via the HTTP admin API.
Versioning
Versions follow vMAJOR.MINOR.PATCH format. Use ./increment_and_push.sh "description" to bump the patch version, commit, tag, and push.
Nostr Core Library
The vendored nostr_core_lib/ is a static library (libnostr_core_x64.a). To rebuild it:
cd nostr_core_lib && ./build.sh --nips=001,004,005,006,011,013,017,019,021,042,044,046,059,060,061
Important Notes
- The wizard runs before
main()sets up the CA bundle, sofetch_models_for_llm_config()in the wizard explicitly callsnostr_http_set_ca_bundle(). - ppq.ai does not expose a balance/credits endpoint — the wizard skips balance checks for
"ppq"provider. - The
relay_remove_index()function insetup_wizard.cis unused (relays use toggle-based enable/disable, not removal).