This file provides guidance to Claude Code when working with this repository.
DazScriptServer is a DAZ Studio plugin (.dll/.dylib) that embeds an HTTP server inside DAZ Studio, enabling remote execution of DazScript code via HTTP POST requests with JSON responses.
Requires DAZ Studio 4.5+ SDK (or the Daz Studio 6.25+ SDK) and CMake.
# Basic build (DAZ Studio 4.x, default)
./build.sh
# Common options
./build.sh install --clean # Install to DAZ Studio (must not be running)
./build.sh build --clean --debug # Clean debug build
./build.sh release v1.3.0 # Create GitHub release
# DAZ Studio 6.x (Qt6) build
./build.sh build --sdk-version 6 --cleanSet DAZ_STUDIO_EXE_DIR in .env for automatic installation. Default: C:\Program Files (x86)\DAZ\Studio4\plugins\
SDK6 changed how Daz distributes the dev kit: it ships dzcore/dzsdkmemory
only — no Qt .lib/.cmake files at all. Building against SDK6 requires a
separate Qt6 devkit matching the Qt6 minor version DAZ Studio 6 bundles
(check Qt6Core.dll's file version in the DAZ Studio 6 install dir). Install
one via aqtinstall (scriptable, also
used in CI):
pip install aqtinstall
aqt install-qt windows desktop 6.10.3 win64_msvc2022_64 -m qt5compatThen set DAZ_SDK_DIR_V6 and QT6_DIR in .env (see .env for the exact
variable names/format) and run ./build.sh build --sdk-version 6. At
runtime the plugin resolves Qt6 symbols against DAZ Studio 6's own bundled
DLLs already loaded in-process — the separate devkit is only needed to link
at build time. --sdk-version 4 and --sdk-version 6 use separate build
directories (build/ vs build-sdk6/), so switching between them never
reuses a stale CMake cache from the other SDK.
Test clients: test-simple.py, tests.py, test-client.js, test-client.ps1
- Main Qt thread: GUI, script execution via
DzScript, all Qt/DAZ API calls - HTTP thread:
ServerListenThreadblocks onhttplib::Server::listen()
IMPORTANT: HTTP handlers run on raw std::threads (not Qt threads). Handlers must do minimal work (parse body), then invoke handleExecuteRequest() on main thread via Qt::BlockingQueuedConnection. All QScriptEngine, DzScript, and Qt operations MUST happen on the main thread.
HTTP handler → validation/auth → emit signal → handleExecuteRequest() (main thread) →
- Check concurrent limit (429 if exceeded)
- Check IP whitelist if enabled (403 if blocked)
- Check rate limit if enabled (429 if exceeded)
- Validate body size (413 if > 5MB default)
- Validate token if auth enabled (401 if invalid)
- Generate request ID (8-char UUID)
- Parse JSON with
QScriptEngine(400 if malformed) - Validate input (script length, scriptFile/script presence, file exists)
- Wrap script in IIFE, inject
argsas JSON literal - Capture output via
onMessagePosted() - Execute via
DzScript, measure duration - Update metrics, log request
- Return
{ success, result, output[], error, request_id }
| Class | Location | Purpose |
|---|---|---|
DzScriptServerPane |
src/DzScriptServerPane.cpp |
Main pane: GUI + server + request handling |
ServerListenThread |
src/DzScriptServerPane.cpp |
QThread wrapper for httplib |
SecureRandom |
src/SecureRandom.cpp |
Crypto-secure RNG (OS APIs) |
JsonBuilder |
src/JsonBuilder.cpp |
Type-safe JSON with auto-escaping |
ServerConfig |
include/DzScriptServerPane.h |
Centralized config constants |
Sync: /status, /health, /metrics, /execute, /scripts/*
Async: /execute/async, /scripts/:id/async, /requests/*
Default: 127.0.0.1:18811
Status codes: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden/IP blocked), 413 (Body too large), 429 (Rate limit/concurrent limit)
Authentication: Token via X-API-Token header or Authorization: Bearer <token>. Auto-generated securely, stored in ~/.daz3d/dazscriptserver_token.txt (chmod 600).
POST /execute accepts:
scriptFile: Absolute path to.dsafile (forinclude()/getScriptFileName())script: Inline DazScript codeargs: Optional object, accessible viagetArguments()[0]
Script Registry (in-memory, session-only):
POST /scripts/register:{"name":"id","description":"...","script":"..."}(name is ID, overwrites existing)POST /scripts/:id/execute: Execute registered script- Script names: 1-64 chars, alphanumeric/hyphens/underscores
Async endpoints: Return request_id immediately. Poll /requests/:id/status or use GET /requests/:id/result?wait=true for long-poll. Status: queued, running, completed, failed, cancelled. TTL: 1 hour cleanup.
- DAZ Studio SDK 4.5+: Qt 4.8,
dzcore - cpp-httplib: Header-only (
src/httplib.h), compression disabled - Windows: Links
ws2_32,advapi32(CryptoAPI) - Unix/macOS:
/dev/urandomfor SecureRandom - MSVC flags:
/MD /U_DEBUG(force MT runtime, disable debug macros)
Authentication: 32-byte hex tokens (128-bit) via SecureRandom (OS crypto APIs). Stored in ~/.daz3d/dazscriptserver_token.txt (chmod 600). Thread-safe (loaded once, read-only).
IP Whitelist: Exact match, comma-separated list. Default disabled. Checked after concurrent limit, before rate limiting/auth.
Rate Limiting: Per-IP sliding window (default: 60 req/60s). QMutex-protected. Periodic cleanup (every 100 requests).
Input Validation:
- Body size: 1-50 MB (default 5MB) → HTTP 413
- Script length: 100-10240 KB (default 1MB) → HTTP 400
- JSON parsing with line numbers
- Script file: absolute path, exists, is file
Request Management:
- Concurrent limit: 5-50 (default 10) → HTTP 429
- Request IDs: 8-char UUID
- Thread-safe counters (mutex-protected)
- Logging format:
[HH:mm:ss] [client_ip] [status] [duration_ms] [request_id] script_identifier
Observability: /health and /metrics endpoints. All settings persist via QSettings.
Limits: 10 concurrent requests, 5MB body, 1MB script, 60 req/60s rate limit Constants: 1000 log lines, 10000 captured lines, cleanup every 100 requests Auth: Enabled by default, token auto-generated Whitelist/Rate limit: Disabled by default
See README.md for full API reference and FUTURE_ENHANCEMENTS.md for planned features.
This project uses bd (beads) for issue tracking. Run bd prime to see full workflow context and commands.
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work
bd close <id> # Complete work- Use
bdfor ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists - Run
bd primefor detailed command reference and session close protocol - Use
bd rememberfor persistent knowledge — do NOT use MEMORY.md files
Architecture in one line: issues live in a local Dolt DB; sync uses refs/dolt/data on your git remote; .beads/issues.jsonl is a passive export. See https://github.com/gastownhall/beads/blob/main/docs/SYNC_CONCEPTS.md for details and anti-patterns.
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds