Skip to content
eidola-aiPublic

About

Artificial intelligence, human agency

Resources

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,426 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Eidola

This is an early work in progress. This repository is intended as a monorepo for Eidola, and will include many different kinds of files, from functional code to configuration to documentation.

Over the coming months, its shape will change substantially as we port several external proofs-of-concept into a coherent, maintainable single source of truth.

Licensing

Eidola is open source under per-component licenses: the server is AGPL-3.0-only, the app layer (app core, GUI, CLI) is GPL-3.0-only, and the reusable libraries, utilities, and everything else default to MIT OR Apache-2.0; documentation and website content are CC-BY-4.0. LICENSE.md is the full map, including the trademark notice and the carve-out for Eidola, Inc.'s legal documents.

Documentation

For an explanation of how Eidola works, what it commits to, and what it doesn't yet defend against, start with docs/paradigm.md. The full documentation index is at docs/README.md, and includes:

Contributing

Pull requests are checked for CLA coverage. Every Git author and committer email in a PR must match an entry in CLA-SIGNERS.txt for the current hash of CLA-INDIVIDUAL.md or CLA-CORPORATE.md.

To sign, add the appropriate entry to CLA-SIGNERS.txt in a commit to this repository. The signer entry plus the relevant Git history are the signature record; there is no separate PDF or email flow.

Developing

Prerequisites: rustup, direnv, docker

The Rust toolchain version is pinned in rust-toolchain.toml and installed automatically by rustup. On first cd into the repo, direnv builds the pinned Rust-based dev tools (including just itself) from crates/devtools/ and puts them on PATH, so rustup + direnv is the whole setup — no separate just install. Run just to see all available recipes.

All Rust workspace packages live under crates/, including the code generation binary generate-openapi and operational utilities such as tinfoil-shim-mock, hash-secret, and measure-enclave. crates/devtools/ is an anchor crate that version-pins upstream Rust-based dev tools — just, the rumdl markdown linter, and cargo-deny — in Cargo.lock; .envrc builds them and direnv exposes them on PATH. just check runs rumdl and cargo-deny, and just lint-md-fix applies auto-fixes.

Server

The server requires environment variables to work correctly. See .env.example. For local development, DATABASE_URL=postgres://eidola@localhost/eidola uses the plain Postgres container from compose.yaml. For production, you can point DATABASE_URL at an external Postgres, set DATABASE_PASSWORD as a secret, and optionally provide DATABASE_SSL_CERT with the PEM-encoded root CA certificate if the database does not chain to the default WebPKI roots.

There are two supported development workflows:

1. Full container stack — postgres, server, shim, and stripe-cli all run inside docker (detached). Use this when you want a one-shot reproducible environment and don't mind rebuilding the server image:

just dev
docker compose logs -f       # follow logs
just down                    # stop everything

2. Host-mode server — postgres, the tinfoil shim mock, and stripe-cli run in containers; the server runs on the host with cargo. The shim is configured to forward to host.docker.internal:8080, so requests from the shim and Stripe webhooks all flow into the cargo-built server. This is the recommended inner loop while iterating on eidola-server:

# Bring up postgres + shim + stripe-cli; captures the Stripe webhook secret
# and writes .env.local. Stripe forwarding is enabled iff STRIPE_API_KEY is
# set in your environment or .env.
just services

# Load env vars (the .env.local override sets BIND_ADDR=0.0.0.0:8080 and
# STRIPE_WEBHOOK_SECRET so the host server is reachable from the shim).
set -a; source .env; source .env.local; set +a

# Run the server on the host machine with cargo
cargo run -p eidola-server

# -- OR --

# Run and automatically recompile/reload the server on the host machine with bacon
bacon run-long -- -p eidola-server

# When you're done:
just down

Both modes expose the same endpoints externally: postgres on localhost:5432, the shim on https://localhost:8443, and (in container mode) the server on http://localhost:8080. CLI configuration is identical across the two modes.

CLI

To run the CLI against a local development stack:

just client-local performs steps 2 and 3 below — it reads the shim's cert set, writes the overrides, and prints (never runs) the one sudo command that has to trust the mock TLS root. just client-reset clears every override back to the built-in trust-root pins. The steps below are what those recipes automate, and the reference for doing it by hand. Both the GUI and the CLI read one profile, so either recipe configures both — quit any running Eidola first, since the local database is single-writer.

  1. Start the stack: just dev (starts Postgres, Server, and the Hardware Shim).

  2. Trust the Mock TLS Root: On its first boot the shim generates two independent root CAs in ./.dev-certs/:

    • tls-ca.pem + tls-ca.key — the TLS trust anchor (RSA PKCS#1 v1.5). This is what you trust in your OS keychain.
    • ark.pem + ark.key + ask.pem + ask.key — the SEV-SNP attestation chain (RSA-PSS, required by AMD's attestation format). These are not TLS roots and should not go in your keychain; they are passed to the CLI via --hardware-root-ca / --hardware-intermediate-ca instead.

    The split exists because Apple's Security framework does not support RSA-PSS in chain validation, so the SEV-SNP chain (which the sev crate requires to be PSS) cannot double as the TLS trust anchor. The shim reuses all six files on every subsequent boot, so once you trust tls-ca.pem your OS keychain entry survives shim restarts. The trust root flows from the filesystem only — never from the shim's API. To rotate the roots, delete .dev-certs/ and re-run the steps below.

    Trusting tls-ca.pem is a machine-wide grant that outlives the stack: whoever can read .dev-certs/tls-ca.key can mint a certificate this machine accepts for any host. The shim writes its private keys 0600 (and tightens an existing key it finds more permissive), but remove the trust when you are done with local dev — just client-reset prints the command.

    • macOS (terminal): sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain .dev-certs/tls-ca.pem
    • macOS (UI): Open Keychain Access, drag tls-ca.pem into the System keychain, double-click it, expand Trust, and set "When using this certificate" to Always Trust.
    • Linux: sudo cp .dev-certs/tls-ca.pem /usr/local/share/ca-certificates/eidola-dev.crt && sudo update-ca-certificates
  3. Configure the CLI:

    cargo run -p eidola-cli -- configure \
      --base-url https://localhost:8443 \
      --hardware-root-ca .dev-certs/ark.pem \
      --hardware-intermediate-ca .dev-certs/ask.pem \
      --trust-measurement 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

    --trust-measurement takes a <snp>:<rtmr1>:<rtmr2> triple — three 96-char hex strings separated by colons — since each Tinfoil release ships paired AMD SEV-SNP and Intel TDX measurements. The mock shim advertises all-zeros for every field, so the dev triple is just three zero blocks. Both --hardware-root-ca and --hardware-intermediate-ca are required when pointing the CLI at the local mock shim — without ASK, the verifier falls back to AMD's production Genoa ASK, which obviously isn't signed by your local mock ARK and the chain fails to verify. (If you ever rotate .dev-certs/, re-run this configure command to refresh the embedded certs.)

    The connection + trust bundle these flags set — base URL, hardware CAs, and trusted measurements — is stored on the eidola backend row in the local database (~/Library/Application Support/eidola/eidola.db on macOS); each is a per-column override of the embedded trust-root pin. Other CLI settings (default template and account credentials) live in ~/Library/Application Support/eidola/config.toml.

    To go back to the released service, clear each override — configure --clear-base-url --clear-hardware-root-ca --clear-hardware-intermediate-ca --clear-trusted-measurements (or just just client-reset). A cleared column is the pin, so nothing else is needed. Prefer --clear-trusted-measurements over --untrust-measurement <snp> when reverting: dropping the list by key leaves the column non-NULL, and a list that no longer names the pinned measurement rejects the real enclave. Note that accounts and wallet credentials are per-server: the account in config.toml was issued by whichever server you were pointed at, so expect to create one on the local stack (eidola account create --accept-terms) and to hit the mismatch in reverse on the way back.

Consider installing bacon (cargo install bacon) for convenience.

Local inference

The client can run models entirely on-device through its bundled inference engine (a statically-linked llama.cpp llama-server, shipped inside every release desktop artifact) — no account, credentials, or network required for the inference itself, and no system llama.cpp install. For dev builds the sidecar isn't produced by cargo: run just engine once and point EIDOLA_LLAMA_SERVER at crates/eidola-gui/build/llama-server/bin/llama-server (or set llama_server_path in config.toml). Then:

cargo run -p eidola-cli -- model list                   # curated Gemma 4 catalog + installed models
cargo run -p eidola-cli -- model download gemma-4-e2b   # or any .gguf / Hugging Face file URL
cargo run -p eidola-cli -- chat "hello" --model gemma-4-E2B_q4_0-it@local

Local models are identified as <file-stem>@local — the same <model>@<backend> form every configured backend uses — and stored under the app data directory (~/Library/Application Support/eidola/models/ on macOS). Every downloaded model is selectable; a request loads its engine on demand, evicting least-recently-used idle engines when memory runs short (pin a loaded model in Settings → Backends — or eidola model pin — to protect it from automatic unloading). The model picker's "Local" group shows each model's load state, and posts attribute their author as the model's human name over its backend ("Gemma 4 E2B" / "Local").

Backends

Where an ask can be routed is configurable. Besides the built-in Eidola (confidential service) and local (managed on-device models) backends, you can add any OpenAI-compatible server — a self-hosted vLLM/Ollama box, or a conventional provider you choose to trust without confidential-computing guarantees — or point Eidola at a llama.cpp models directory you manage yourself (Eidola starts/stops the engines, never touches the files):

cargo run -p eidola-cli -- backend add openai my-vllm --url http://192.168.1.20:8000 --api-key sk-…
cargo run -p eidola-cli -- backend add llamacpp my-box --models-dir ~/models
cargo run -p eidola-cli -- chat "hello" --model "qwen3-8b@my-vllm"
cargo run -p eidola-cli -- backend disable eidola   # no-account, on-device-only mode

Models on a configured backend are addressed as <model>@<backend-id>. Because not every "OpenAI-compatible" server implements GET /v1/models, a backend's model list can be pinned manually (--models a,b). In the GUI, Settings → Backends manages all of this, and the model picker groups models per backend with per-backend retry/refresh.

GUI

Build, run, and test the gpui-based desktop app (macOS 13 Ventura or later, or Linux under Wayland) from the command line:

just build gui      # cargo build + package Eidola.app (on macOS)
just run gui        # build and open Eidola.app (macOS) / run the binary (Linux)
just test           # cargo test

On macOS, just build gui produces crates/eidola-gui/build/Eidola.app; on Linux, just run gui runs the binary directly (the Linux GUI is Wayland-only). See crates/eidola-gui/AGENTS.md for the architecture details.

The local on-device backend runs a bundled, statically-linked llama.cpp llama-server — release desktop artifacts ship it, so no system llama.cpp install is needed. For dev, just engine (Nix) materializes the sidecar and just build gui copies it into the dev Eidola.app at Contents/MacOS/llama-server — beside the main binary, the same place the release bundle puts it, since the engine is resolved as a sibling of the running executable. Without it the GUI's Local tab shows an honest missing-engine state (or point llama_server_path at your own build).

Website

The public site (www.eidola.ai) lives in this repo: content in www/, generator in crates/eidola-www (see its README), and the docs section rendered directly from docs/. It adopts the app's circadian theme.

just build www      # build the static site to target/www
just run www        # local dev server at http://127.0.0.1:8000 (drafts included)

Deploys to GitHub Pages on promotion to main via .github/workflows/website.yml; PRs that touch site inputs get a validating build.

See more available commands:

just

Updating generated files:

just update-openapi    # OpenAPI spec
just update-manifest   # artifact-manifest.json (OCI images, desktop binaries, enclave measurements)

This uses the pinned amd64 BuildKit builder configuration for the OCI images, the local Nix build for the host platform's native desktop artifacts (the macOS universal CLI and GUI .app on macOS; the Linux GUI on Linux), and the measure-enclave binary to compute SEV-SNP and TDX measurements from tinfoil-config.yml. The Linux GUI narHash is always regenerated: natively on Linux, and on macOS by reproducing CI's native x86_64-linux build inside a pinned linux/amd64 Nix container (docker required — the same assumption the StageX pipeline already makes). Only the macOS narHashes are host-exclusive (they can't be cross-built from Linux), so on a Linux host they're carried over from the committed manifest; CI verifies the full set on both platforms.

To compute enclave measurements independently (without rebuilding images):

just measure

About

Artificial intelligence, human agency

Resources

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages