Files

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_case for functions and variables, UPPER_CASE for macros
  • Headers: Include guard via #ifndef / #define / #endif
  • Error handling: Return negative error codes, use NOSTR_SUCCESS (0) from nostr_core_lib/nostr_core/nostr_common.h
  • Logging: Use DEBUG_INFO(), DEBUG_WARN(), DEBUG_ERROR() macros from src/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 in main.c and passed through the tool context.
  • Role paths: Uses BIP-44 derivation paths (role_path) instead of the old nostr_index selectors. See docs/SIGNER.md.
  • Wizard: The setup wizard (src/setup_wizard.c) is a plain-text interactive TUI (no ncurses). It uses fprintf(stderr, ...) for output and fgets() 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_tag identifiers. 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, so fetch_models_for_llm_config() in the wizard explicitly calls nostr_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 in setup_wizard.c is unused (relays use toggle-based enable/disable, not removal).