All notable changes to daedalus will be documented in this file.
Format based on Keep a Changelog.
- PyPI:
pip install daedalux(packaging/pip/) - a stdlib-only launcher that downloads the matching release binary on first run (SHA-256 verified againstchecksums.txt), caches it under~/.cache/daedalus/pip/, and execs it. Published to PyPI asdaedalux(the namedaedalusis reserved by PyPI); 9 unit tests; verified end-to-end (venv install → build → run). - Homebrew tap:
Encapsul/homebrew-daedalus-brew install Encapsul/homebrew-daedalus/daedalusinstalls the signed release binaries (daedalus+daedalus-stub+daedalus-crypto), sha256-pinned per platform, verified end-to-end on macOS amd64. cargo install daedalux: daedalus is published to crates.io (daedaluxCLI +daedalux-corelibrary).cargo install daedaluxinstalls thedaedalusbinary; on firstbuildthe matchingdaedalus-stubis downloaded from the GitHub release (SHA-256 verified againstchecksums.txt) and cached under~/.cache/daedalus/stubs/<version>/<arch>/.- Shared GitHub release asset helpers in
daedalus-cli/src/release.rs(release_tags,asset_name,download_release_asset) so the stub fallback and upgrade path agree with the release pipeline naming and checksum format. - Run straight from the registry:
daedalus run http://host:port/artifact/app:v2(orregistry://host:…) downloads a published.de, verifies its footer, caches it under~/.cache/daedalus/downloadskeyed by URL hash, marks it executable, and runs it - re-runs reuse the cache. npx-style app distribution. - Runnable artifact registry: the registry now stores the full
.debinary under aname:tagalias, not just layers.registry push FILE --name name[:tag] [--local DIR | --registry URL]publishes a runnable binary;registry pull --name name[:tag] -o out.defetches it (local and remote). Untagged names default to:latest.serve startgainsGET /artifacts,GET /artifact/<name>:<tag>(binary download), andPOST /artifact?name=&tag=(binary store), plus fullContent-Lengthrequest parsing for multi-MB bodies.build --publish <URL>publishes the finished binary as<file-stem>:latest; local--publish <DIR>still stores the layered manifest.
LayerRegistry::{publish_artifact_binary, artifact_binary, list_artifacts}+StoredArtifactindaedalus-core::registry, backed by an atomicartifacts.jsonindex over the CAS- Lazy-loading priority extraction:
daedalus build --lazy-loadnow fast-starts by extracting only the entrypoint (plus an explicit--lazy-priority file,dir,...list or[build] lazy_priority) before launching, then fills the rest of the app tree in the background. Priority specs may be app-relative (main.py) or payload-relative (app/main.py); directories expand recursively (skipping__pycache__/.git), capped at 2048 files. Dry-run reportsLazy:on/off and the priority count. Namespaced builds (isolation ≥ 2) extract synchronously so the user-namespaceunsharestays single-threaded. daedalus-core::lazy::expand_priority_paths- build-time expansion of--lazy-priorityspecs into payload-relative paths with tar-exclusion parity- Constrained-host auto-disable:
--lazy-loadwithout an explicit priority list turns itself off on small hosts (< 4 cores or < 3 GiB RAM, viadaedalus-core::system_info::is_constrained_host) daedalus inspectreports thelazy_prioritymetadata (Lazy priority:line)- GPU passthrough for accelerated inference:
daedalus build --gpu auto|nvidia|rocm|none(or[build] gpuin.daedalus.toml) records the compute backend in the binary metadata. Whennvidiaorrocmis set, the launcher bind-mounts the GPU device nodes (NVIDIA/dev/nvidia*+ caps, ROCm/dev/kfd+/dev/drirender nodes) into the sandbox via a regular-file overlay and pins the matching visibility vars (CUDA_VISIBLE_DEVICES,NVIDIA_VISIBLE_DEVICES,HIP_VISIBLE_DEVICES,ROCR_VISIBLE_DEVICES) - Ollama/llama.cpp offload works headless, inside the sandbox. daedalus-core::gpudetection probe (--gpu auto):/dev/kfd→ ROCm,/proc/driver/nvidia/gpus→ NVIDIA; CPU fallback is safe (missing nodes are skipped)daedalus inspectshows the embeddedGPU:backend; dry-run reports the resolved backend- Multi-service builds:
--entrypoint name=cmd,arg,...now serializes named services into the binary metadata and the stub supervisor runs them.--service-port NAME=PORTand--service-timeout NAME=SECONDSconfigure the readiness probe (ready_port/ready_timeout) each service is gated on before the app is considered up. daedalus inspectnow lists multi-service definitions (Services:section,service.<name>lines in--plainmode)ServiceSpecindaedalus-core::assemblyserializing{name, cmd, env, ready_port, ready_timeout}- the schema the stub supervisor deserializes
- System config detection module
daedalus-core/src/system_info.rs(SystemConfig,detect(),compute_universal_slices()) for resource-adaptive builds
daedalus upgradematched the wrong release asset: it builtdaedalus-{version}-{platform}.tar.gzand fetched{url}.sha256, but releases usedaedalus_{version}_{os}_{arch}.tar.gzwith a singlechecksums.txt, and the archive has nobin/subdirectory (binaries sit at the archive root). The command now derives the asset fromrelease_tags, parseschecksums.txt, and copies the binaries from the extracted top level. This also fixes theplatformreference in--jsonoutput (undefined after the refactor).registry pushremote was broken: it POSTed the raw binary to the layer/pushendpoint, which the server tried to JSON-parse as a layer (400). Remote pushes now target the correct endpoint - full binaries go to/artifact, layers to/pushas JSON.registry pushrejected.defiles (extension check still required.daedalusafter the 0.6.0 rename); both extensions are accepted now--lazy-loadruntime failure: priority files used absolute/app/...paths that never matched the tar's payload-relative entries (and wrongly listed the host PATH interpreter as a payload file), so every lazy build errored withpriority files not found in payload: /app/python3. Priorities are now payload-relative and the interpreter is never one.- Stub lazy extraction skipped non-priority tar entries without draining them, misaligning the stream and breaking extraction with
Invalid argument (os error 22)whenever a non-priority entry was followed by more data - entries are now drained before skipping - Stub multi-service supervisor on Unix now execs via
execvp(PATH search) instead ofexecve, so bare interpreter names likepython3resolve at runtime - multi-service binaries can actually launch their services find_in_bin_paths/is_executable_pathmade cross-platform (were Windows-only), so the Unix supervisor shares the same interpreter-resolution fallback as the Windows and single-service paths- Removed unused
libc_execveFFI declaration andenv_to_cstringshelper daedalus-core/src/lib.rs: restoredpub mod paths;(was accidentally replaced when addingsystem_info)
- Per-runtime install-tree embedding:
embed_runtime_dir()copies filtered runtime directories (Python DLLs/stdlib, Node modules, Electron assets) instead of single binary only, enabling fully self-contained.debinaries across platforms RuntimeProfilewithonly/excludefilters per runtime (python, node, electron have filters; deno/hugo/wasmtime self-contained)tools_dir_for()+target_suffix()+interpreter_bin()centralized binary resolution indeps.rsresolve_cross_interpreter()extracted fromembed_interpretersto reduce clippytoo_many_linespython_host_install_dir()viasys.prefixon Windows;Noneon Linux/macOS (deps resolved byldd+embed_python_config)--embed-interpreter pythonCLI flag + CIsmoke-test-windowsjob updated
is_crosslogic in pipeline:target.is_some_and(|t| parse_target(t).0 != host_arch)(was comparingtarget_archstring incorrectly on host builds)- Clippy
needless_returnremoved from#[cfg(not(unix))]blocks inembed.rs - README: removed "Two distinct segments" enterprise impact breakdown; de-duplicated AI model packaging section (H1→H2)
embed_runtime_dir()indaedalus-core/src/embed.rswithcopy_runtime_filtered()andmatches_pattern()RuntimeProfile::self_contained()const fn for self-contained runtimesfind_python_in()helper for Windows Python discoverywhich("python")fallback inensure_pythonfor Windows hosts
- File extension: renamed from
.daedalusto.deacross CLI, docs, and examples - Command rename:
upgrade-binary→migratefor clarity - Error messages: improved to include problem + cause + fix (e.g. "app directory not found" instead of "failed to canonicalize app path")
- AES-256-GCM payload encryption: optional external-key encryption for
.debinariesdaedalus build --encrypt <keyfile>encrypts the payload at build time with AES-256-GCM + HKDFdaedalus run --decrypt-key <keyfile>decrypts the payload at runtime- Encryption metadata (salt, nonce, tag offset, encrypted size) is stored in the JSON metadata block
- The binary never contains the key; key rotation is supported by re-encrypting
- Extension consistency between HN pitch (
.de) and CLI output (.de) - Multi-arch release pipeline: glow-style GitHub releases with per-platform archives
- Linux amd64/arm64/386/arm/riscv64/ppc64le (musl + gnu)
- Darwin amd64/arm64
- Windows amd64
- Automated checksums.txt generation
- Runtime detection & build robustness
- Python: detect projects with
.pyfiles at root (sqlmap case) - Node: fallback to
npm installwhen no lockfile is present - Rust: gracefully skip library/workspace crates without
[[bin]]
- Python: detect projects with
- External tool download URLs
- Deno: correct asset naming (
<arch>-<triple>) - Hugo: include version from GitHub API in asset name
- Deno: correct asset naming (
- Symlink handling: preserve symlinks during directory copy to avoid infinite loops
- Release pipeline: glow-style GitHub releases with per-platform archives and checksums
- Dependency updates: upgrade lru to 0.16.4, ratatui to 0.30, and other security fixes
- CI failures: resolve clippy
must_use_candidatewarnings and stubmissing_docswarnings - Release workflow: ensure GitHub releases are created with changelog notes and all artifacts
- Documentation: audit and fix Unix man-page doc comments across daedalus-cli and daedalus-stub
- Windows embedding: skip
ldddependency resolution on non-Unix so the interpreter (e.g. officialnode.exe) is embedded self-contained instead of aborting the build - i386 stub build: gate socket syscalls behind
SYS_socketcallon 32-bit x86 so every architecture in the release matrix builds
- Release pipeline: glow-style GitHub releases with per-platform archives and checksums
- Dependency updates: upgrade lru to 0.16.4, ratatui to 0.30, and other security fixes
- CI failures: resolve clippy
must_use_candidatewarnings and stubmissing_docswarnings - Release workflow: ensure GitHub releases are created with changelog notes and all artifacts
- Documentation: audit and fix Unix man-page doc comments across daedalus-cli and daedalus-stub
- SISR self-update engine: a
.daedaluscan update itself from signed deltasdaedalus build --self-updateenables the engine;daedalus upgrade-binarymigrates existing binaries- Incremental updates: manifests list changed chunks with Ed25519 signatures and a Merkle root; the launcher fetches, verifies, rebuilds and atomically swaps the binary in place
- Health gate with automatic rollback: a crashing new version is quarantined and the previous one is re-executed
- Update URL resolution:
--daedalus-update <URL>>$DAEDALUS_UPDATE_URL> embeddedmeta.update_url
- Transparent v1 → v2 runtime migration for legacy binaries
- Cross-compilation support (
--target aarch64, ...) with per-arch Node.js download and N-API addon embedding - Landlock filesystem sandboxing (Linux, kernel ≥ 5.13)
- Laravel Octane (RoadRunner) detection and
rrembedding - Ruby native gem
.sodependencies embedded vialddscan - PHP version constraint checking in
composer.json - pnpm
.pnpmstore excluded during copy (avoids massive duplication) daedalus publishcommand; pip retry/timeout for proxy resilience- Clean remote cache design + single app-hash cache key
- E2E SISR tests, network fault injection, and property-based fuzzing
- Release pipeline: multi-platform builds on native runners (linux amd64/arm64
- extended musl/gnu targets, darwin amd64/arm64),
checksums.txt, publish viagh release
- extended musl/gnu targets, darwin amd64/arm64),
- Auto-download PHP extensions from
shivammathur/php-builderGitHub releases - PHP extension detection from
composer.json(ext-*requirements) - Shared library bundling for downloaded PHP binaries (libssl, libgd, etc.)
make disttarget for multi-arch release builds (x86_64, aarch64)make releasetarget for GitHub release creation- Node.js auto-download now caches in
~/.cache/daedalus/build-tools(mode 0700) instead of a world-writable/tmpdirectory
- Standardized release asset naming (glow-style):
daedalus_<version>_<os>_<arch>.<ext>withchecksums.txt - Launcher hardened: all remaining
unwrap()/expect()calls in the stub converted toResulthandling - the launcher never panics on malicious input
- Ubuntu detection in
detect_linux_distro()- Ubuntu's/etc/os-releasecontainsID_LIKE=debian, which caused downloading the wrong PHP build - Missing shared libraries for downloaded PHP binaries at runtime
- Dead code: removed unused
download_php_extension()(replaced by binary download) - Isolation level 2 always failed with
ENOENT:- Embedded runtimes shipped without their dynamic loader (
ld-linux.so), so the kernel could notexecthem afterpivot_root. The ELF interpreter is now bundled into the rootfs. execvpresolved relativePATH/LD_LIBRARY_PATHentries against the post-pivot_rootcwd (/app). Absolute paths are used in the pivot branch.go/binaryruntimes were wrapped inbash, which was not bundled; they are now executed directly.
- Embedded runtimes shipped without their dynamic loader (
--isolationnow fails closed: unknown values are rejected instead of silently downgrading to level 1- Signing keys are zeroized (
zeroize) after use duringkeygenandsign
- ANSSI-Rust security fixes: deduplicated FFI wrappers, shared helpers, audit fixes
- Documentation updates: corrected stale Python CLI references, version badges, unsafe rules
- Cron jobs (
--cronflag) - OpenTelemetry integration (
--otelflag) - Tree-shaking (
--treeshakeflag) - HTML/JS/CSS minification (
--minifyflag) - Custom health check port (
--health-portflag) - Persistent storage (
--persistflag) - Extra file inclusion (
--includeflag) - Supervisor mode for multi-service apps
- Single-service exec uses
execvp(PATH lookup) instead ofexecve
- Full Rust CLI - zero Python dependency at runtime
daedalus build- package any app into self-extracting ELFdaedalus inspect- read metadata from .daedalusdaedalus scan- find .daedalus files recursivelydaedalus sign/daedalus verify- Ed25519 signing & verificationdaedalus keygen- generate signing keypairsdaedalus trust- manage trusted public keysdaedalus doctor- system health checks (--strictfor CI)daedalus env- show environment & build configdaedalus clean- remove cachedaedalus completion <shell>- shell completions (bash, zsh, fish, elvish, powershell)daedalus man [dir]- generate man pages
.daedalus.tomlconfig file - defaults for all build flags- Shell completions -
daedalus completion bash/zsh/fish > file - Man pages -
daedalus man /usr/local/share/man/man1/(pre-generated in release tarballs)- Full Unix man(7) sections: EXIT STATUS, ENVIRONMENT, FILES, SEE ALSO, AUTHORS, HISTORY, BUGS
--env KEY=VALUE- repeatable flag to bake environment variables into the binary--env-file- load KEY=VALUE pairs from a file (now actually works)- 10 integration tests for Rust CLI
- 100% Rust core - format, compression, detection, signing, assembly, tar, pkgmgr
- Benchmark script -
benchmarks/run-bench.shfor measuring build performance
- Bumped version from 0.2.9 → 0.3.0
- daedalus-stub cross-compile target:
aarch64-unknown-linux-musl→aarch64-unknown-linux-gnu - daedalus doctor non-fatal by default, use
--strictfor CI - Release workflow rewritten to match toboggan working pattern
- find_binary now searches workspace-level
target/directory - Single-service exec uses
execvp(PATH lookup) instead ofexecve - Node.js entrypoint detection: checks package.json "main", "scripts.start", then fallback list
- Sign command wrote corrupt V3 footer - footer was written at the same offset as sig_block, overwriting it; now writes [sig_offset:u64le][core:84] after sig_block with proper set_len
- Sign command opened file read-only - changed
File::opentoOpenOptions::new().read(true).write(true) - Verify panicked on sig_size mismatch - now validates sig_size==64 and uses fixed slice
--env-fileresolved but never loaded - the path was resolved but the file was never read; now parses KEY=VALUE lines- SHA-256 integrity check - stub now always verifies
SHA-256(payload || meta_bytes), matching what the builder computes - Long tar paths - removed manual
set_path()on GNU header (100-byte limit) for npm deep deps - Entrypoint resolution - generated argv is now runtime-aware (Python:
["python3", "/app/app.py"], Node:["node", "/app/index.js"]) cwd=/appset in metadata so stubchdirs before execnode_modulesincluded with--no-install- copy filter respects the flag- find_binary now searches workspace-level
target/directory - Python CLI
--versionreads from pyproject.toml (was hardcoded "0.1.0") - Version centralization: single source of truth in pyproject.toml
- Shell completion: bash/zsh/fish scripts generated at build time
- daedalus-stub and daedalus-crypto marked optional in doctor checks
- Python CLI shell completion (bash, zsh, fish)
- Python CLI
--strictflag for doctor - find_binary searches workspace-level target/ directory
- Multi-arch GitHub releases (Linux x64/arm64, macOS x64/arm64)
- Release workflow triggered on GitHub release publish
- SSH signing for all commits/tags
daedalus doctorhealth check commanddaedalus envenvironment info command