daedalus packages an app into one self-extracting Linux ELF. The file carries
a launcher, the app, and a runtime when the build embedded one. On Linux you
run that file. The CI smoke that proves this is examples/hello-web: a Python
standard-library HTTP server, built and curled on every Linux run.
The CLI itself builds on Linux, macOS, and Windows. The artifact does not. Running it on macOS or Windows takes WSL or a Linux VM.
On every launch the stub reads the footer, checks SHA-256, extracts into
~/.cache/daedalus/<hash>/, then execs the entrypoint. That extract is why
the first start is slower than AppImage or pkg. One measured pass of a 312-byte
Node server (benchmarks/comparison/results/codespaces-ba69ea, Node 24):
| Packager | Artifact | On disk after launch | Cold start |
|---|---|---|---|
| AppImage | 24.4 MiB | 24.4 MiB | 29 ms |
| pkg | 70.2 MiB | 70.2 MiB | 109 ms |
| daedalus | 44.0 MiB | 122.6 MiB | 2.1 s |
| Docker | 76.7 MiB | 76.7 MiB | 6.1 s |
daedalus warm start on that pass was 96 ms. Docker needs a daemon. Mounting a SquashFS without extracting is not implemented. Details and how to reproduce: benchmarks/comparison.
Signing is on by default with a dev key generated on the build machine and
trusted there. daedalus verify on that same machine succeeds because of
that auto-trust. Another machine trusts the file only after daedalus trust
of the public key. Optional AES-256-GCM keeps its decryption key in the file
metadata so the stub can decrypt. The Ed25519 private key is not in the file.
SISR can ship a delta instead of the whole file. The "~90% smaller" number in
older notes is a synthetic 200 MiB blob with 10% of its bytes flipped. A bench
on a real pair of model weights exists and is marked --ignored.
Names: the command is daedalus. The crates.io and PyPI packages are
daedalux because daedalus was already taken. daedalus build writes
app.de unless -o says otherwise. scan, publish, and registry accept
.de and .daedalus.
# Install - any one of these. The shell installer checks sha256 and refuses
# to continue if checksums.txt has no matching line.
cargo install daedalux
brew install encapsul/daedalus/daedalus
pip install daedalux
curl -fsSL https://raw.githubusercontent.com/Encapsul/daedalus/main/install.sh | sh
# Package the app the CI actually smokes. Default output name is app.de.
# The build copies python3 from this machine into the file.
daedalus build ./examples/hello-web -o hello-web.de
# Run it on Linux. The CI smoke does this on the same runner that has Python.
./hello-web.decargo install daedalux ships only the CLI; on first daedalus build it downloads
the matching stub from the GitHub release (SHA-256 verified against checksums.txt)
and caches it under ~/.cache/daedalus/stubs/. The Homebrew tap and PyPI package
ship the CLI, stub, and crypto tools together.
Detection order matches Runtime in
daedalus-core/src/detect.rs. Keep the two in
sync. "Detected" means the builder recognizes the project. It does not mean a
framework listed in the third column has a passing end-to-end test in CI.
Evidence, as of this tree:
- CI smoke (build, run, HTTP 200): Python standard library,
examples/hello-web, on the Linux workflow. The macOS and Windows workflows build the same example. - Workspace e2e, when the toolchain exists: Rust (
daedalus-cli/tests/rust_build.rs, cargo is on the CI image). Java/Gradle (java_build.rs) skips unlessjavaand Gradle ≥ 7 are installed. CI does not install them. - Source in
examples/, not the CI smoke:hello-node,bottle-web,bottle-web-pip,hello-zig,hello-dart,hello-lua. - Scaffold:
examples/offline-health-agriis a standard-library script plus aModelfile. It does not contain model weights. If Ollama is already running it querieslocalhost:11434. Otherwise it prints the prompt it would have sent.
| Runtime | Detection | What the code looks for | Evidence |
|---|---|---|---|
| Python | requirements.txt, pyproject.toml, Pipfile |
Django, FastAPI, Flask, Streamlit markers | CI smoke is stdlib HTTP only |
| Deno | deno.json, deno.jsonc |
Fresh | Detection |
| Node.js | package.json |
Next.js, Express, NestJS, Fastify, Hono | Example + one comparison bench |
| Electron | package.json with electron dep |
Generic Electron app | Detection |
| Flutter | pubspec.yaml with flutter SDK dep |
Release bundle from lib/main.dart |
Needs the Flutter SDK on PATH. No SDK download |
| Dart | pubspec.yaml without the flutter SDK dep |
AOT-compiled bin/main.dart |
Example in repo |
| Java | pom.xml, build.gradle |
Spring Boot | e2e test, skipped without java+gradle |
| Ruby | Gemfile, _config.yml |
Rails, Sinatra, Jekyll | Detection |
| .NET/C# | *.csproj |
ASP.NET | Detection |
| Rust | Cargo.toml |
Static binary | Workspace e2e |
| Zig | build.zig, build.zig.zon |
zig build -> static binary |
Example in repo |
| Go | go.mod |
Static binary | Detection |
| PHP | composer.json |
Laravel, Symfony, WordPress | Detection |
| Perl | Makefile.PL, cpanfile |
Mojolicious layout only | Detection |
| Lua | *.lua, lua in package.json |
OpenResty, Neovim plugins | Example in repo |
| Hugo | hugo.toml, config.toml |
Static sites | Detection |
| Ollama | Modelfile, models/*.gguf, ollama in package.json, or DAEDALUS_OLLAMA=1 |
ollama serve |
Detection. Weights are whatever is in the project |
| Gemma | Modelfile with a Gemma FROM, or models/gemma*.gguf |
ollama run <model-id> |
Detection. The in-repo example has no weights |
| Wasm | *.wasm |
WASI (wasmtime) | Detection |
| Binary | ELF/PE executable | Any native binary | Detection |
[stub][payload][metadata][footer]
- Stub: statically-linked launcher (Linux ELF; musl, so it owns no dynamic
dependencies - the only host dependency is the Linux kernel). It reads its own
binary, verifies integrity, extracts the payload, and
execvps the entrypoint - Payload: zstd-compressed tar archive (or squashfs) of the application + runtime
- Metadata: JSON with runtime info, entrypoint, layers, capabilities
- Footer: magic
0xBEEF_CAFE, format magicDAE\x01, format version, integrity SHA-256 hash
Format versions: v2 (plain), v3 (signed), v4 (encrypted), v5 (squashfs).
| Command | Description |
|---|---|
build <dir> |
Package an app directory into a .de file |
run <file> |
Execute a .de file |
inspect <file> |
Read metadata from a .de file |
scan [dir] |
Find .de files and display metadata |
sign <file> |
Sign a .de with an Ed25519 private key |
verify <file> |
Verify the signature against trusted keys |
keygen |
Generate an Ed25519 keypair |
trust <keyfile> |
Add a public key to trusted keys |
doctor |
Check system prerequisites |
clean |
Remove daedalus cache and build artifacts |
selftest <file> |
Test a .de file in an ephemeral sandbox |
upgrade |
Self-update the daedalus binary |
migrate <input> <output> |
Migrate legacy v1 binary to SISR-enabled v2 |
swap <binary> <layer> <file> |
Hot-swap a layer in a .de binary |
publish <file> |
Publish a .de file to a registry |
| `registry push | pull |
env |
Show daedalus environment info |
completion <shell> |
Generate shell completions |
man [dir] |
Generate man pages |
| Flag | Description |
|---|---|
--verbose / -v |
Enable verbose output |
--quiet / -q |
Suppress non-error output |
--no-color |
Disable colored output |
--plain |
Machine-readable output (no ANSI, no pager, no box drawing) |
--no-input |
Disable all interactive prompts (for CI/scripts) |
--json |
Output as JSON (where supported) |
| Flag | Commands | Description |
|---|---|---|
--target |
build | Cross-compile target. CI-verified (stub built + smoke-tested): linux-x64, linux-arm64, darwin-x64, darwin-arm64, win-x64. win-arm64 is accepted but not built by CI. Any Rust triple is parsed; the short forms are conveniences. |
--sign --key <file> |
build | Sign the binary with Ed25519 |
--encrypt <keyfile> |
build | Encrypt payload with AES-256-GCM |
--enable-sisr --update-url <url> |
build | Enable delta updates |
--dry-run |
build, scan | Preview without doing anything |
--output / -o |
build, inspect, scan | Output file/directory |
--strict |
doctor | Exit with error if any check fails |
--force |
clean, sign, migrate | Skip confirmation prompts |
--local <dir> |
registry | Use local directory instead of HTTP registry |
- Rust toolchain (stable, 2021 edition): https://rustup.rs
- C compiler (
gccorclang) - On Linux:
musl-toolsfor static linking# Ubuntu/Debian sudo apt install musl-tools gcc rustup target add x86_64-unknown-linux-musl # Fedora sudo dnf install musl-gcc rustup target add x86_64-unknown-linux-musl
git clone https://github.com/Encapsul/daedalus.git
cd daedalus
cargo build --release
# Binary at target/release/daedalus| Crate | Purpose |
|---|---|
daedalux-core |
Shared library: format, compression, detection, signing, assembly (published crates.io as daedalux-core) |
daedalus-stub |
Self-extracting launcher (Linux ELF only today; not published to crates.io) |
daedalux |
CLI tool (cross-platform; published crates.io as daedalux, installs the daedalus binary) |
Place a .daedalus.toml in your app directory. CLI flags override config file values.
[package]
version = "1.0.0"
author = "Your Name"
description = "My awesome app"
[build]
isolation = "sandbox"
seccomp = true
target = "x86_64"
no_install = false
env_file = ".env"Give another machine the public key. Verification on the build machine is not
that proof: the default dev key is written into that machine's trust store
during daedalus build.
daedalus keygen --key-dir ./keys
daedalus build ./examples/hello-web -o hello-web.de --key ./keys/<id>.key
# on the other machine, with only the .pub file:
daedalus trust ./keys/<id>.pub
daedalus verify hello-web.dedaedalus verify with an empty trust directory fails. That is the check you
want before you hand the file to someone else.
- Ed25519: strict verification (
verify_strict, ZIP-215). Signing is on by default.--skip-signopts out.--keyselects the key. - SHA-256: the stub checks the payload hash before extraction. Release
downloads (stub, upgrade, Homebrew, PyPI,
install.sh) are checked againstchecksums.txt.scripts/install.shis the older installer. It now refuses to install when that check cannot run. Prefer the rootinstall.sh. - AES-256-GCM: optional (
--encrypt). The decryption material is in the file metadata, because the stub has to decrypt. It stops a casualtarof the payload. It does not hide the app from whoever can run the file. - Isolation: on Linux, user and mount namespaces plus
pivot_rootwhen the kernel allows it. seccomp is a denylist of 18 syscalls (ptrace,bpf,userfaultfd,mount, module loading,reboot, …). Networking, file I/O, andexecstay available unless capabilities say otherwise. This is not an application sandbox. If seccomp cannot be installed, the launcher warns and continues. macOS has an App Sandbox profile path. Windows has process isolation. See docs/src/security.md. selftest: runs the file from a temporary directory so you can see it start before you keep it.- SISR: a delta is applied only when its Ed25519 signature checks.
- SBOM:
daedalus inspect --sbomlists the recorded contents. - Attestations:
daedalus attest app.designs an in-toto statement over the SBOM and the SHA-256 of the whole file, stub included.daedalus attest app.de --verifychecks that statement against the local trust store.
What daedalus attest actually guarantees, and what it does not:
- Covers the SBOM. The signature is computed over a canonical envelope of
(file digest, SBOM), so changing a version string in the SBOM invalidates the signature. An attacker cannot substitute a false inventory. - Covers the whole file, stub included. The digest is over every byte of the
.de, not just payload and metadata, so a substituted launcher is detected. (daedalus signsignspayload || metadata || footeronly; the attestation is deliberately broader.) - Strict verification.
verify_strictrejects small-order keys and non-canonical signatures per ZIP-215. - Offline. Verification needs only a trusted public key. No CA, no network, no transparency log, no clock.
- Not a timestamp or a transparency guarantee. An attestation says who signed what bytes; it does not prove the key was not compromised, nor that no other valid signature exists for different bytes. There is no revocation list. If you need key lifecycle, manage keys outside the tool.
Tests that enforce the above: signature_covers_sbom_so_tampering_breaks_verification,
file_digest_covers_the_stub_region, strict_verification_rejects_small_order_key_zip215
in daedalus-cli/src/commands/attest.rs.
Trust model: distributes through the same channels auditors already trust (crates.io, Homebrew, PyPI) rather than piping random binaries, and ships the verify/sign/trust story built in, not as an afterthought.
# Format check
cargo fmt --check
# Lint (per-crate, see AGENTS.md)
cargo clippy -p daedalux-core --all-targets -- -D warnings
cargo clippy -p daedalus-stub --all-targets -- -D warnings
cargo clippy -p daedalux --all-targets -- -D warnings
# Tests
cargo test --workspaceSee CODE_STYLE.md for Rust style guidelines and AGENTS.md for build constraints.
- Fork the repository
- Create a feature branch (
git checkout -b feat/my-feature) - Commit with signed commits (
git commit -S -m "feat: ...") - Push and open a pull request
MIT - see LICENSE for details.
- GitHub Issues - report bugs or request features
daedalus feedback --browser- open the feedback page quickly