Hologram Model Hub

Hologram Model Hub

@uor2RustApache-2.0Updated Yesterday

Find open models and get download links with the SHA-256 each file must have.

Server endpointStreamable HTTPNo authProbed

This is the third-party server itself — Odel doesn't run it. Hitting this URL directly talks straight to the upstream server with no auth or proxying. Connect through Odel to front it with managed auth.

Hologram Live

Hologram Desktop console showing local store size, module readiness, and system meters

Hologram Live is a local-first module host for the Hologram ecosystem. This repository produces two independent products:

  • Hologram Server — the standalone hologram binary, containing the CLI and background service.
  • Hologram Desktop — a Tauri application that bundles hologram as a managed sidecar and embeds the shared .holo application executor.

The current desktop experience provides a Console dashboard, multi-thread Chat with archiving, content-addressed Files, watched .holo Applications, and module discovery in a responsive dark/light interface. A Cmd/Ctrl+K command palette reaches every action, and text size is adjustable with Cmd/Ctrl +/-/0. Chat routes through a configurable inference engine: the default echo engine repeats your message, while weightc (one-shot CLI over imported .wcpu artifacts) and Ollama-compatible HTTP endpoints serve real model completions.

Quick start

Desktop application

cd apps/desktop
npm ci
npm run dev

The preparation step builds the server sidecar before Tauri opens. The desktop app can:

  • start, restart, and stop the local Hologram service;
  • create and switch between chat threads with independent, durable histories;
  • upload, rename, list, and download local files;
  • watch application source directories, compile/import their .holo archives, and inspect verified archive metadata;
  • inspect the enabled module catalogue;
  • follow the system appearance or remember a light/dark choice; and
  • remain available from the system menu bar after the main window closes.

Build an installable desktop bundle with:

just desktop-build

The desktop UI's colours, type, and marks derive from the Hologram brand kit at a pinned revision; apps/desktop/BRANDING.md explains how to update the brand everywhere in three steps.

Standalone server

cargo build --release --locked --package hologram-live --bin hologram
./target/release/hologram init
./target/release/hologram start
./target/release/hologram status
./target/release/hologram modules list

Run the service in the foreground instead with:

./target/release/hologram serve

Every server advertises http://127.0.0.1:11435 by default and generates a 256-bit membership secret in its state directory at cluster.token. The file is reused across restarts and is owner-only on Unix. To form a multi-host cluster, securely give every node the same secret (at least 32 bytes), advertise the origin other nodes can reach, and seed a new node with any live member:

# first node; generates <state_dir>/cluster.token
hologram serve --advertise https://registry-a.example.com

# joining node; use the contents securely copied from the seed
HOLOGRAM_CLUSTER_TOKEN='<seed cluster.token contents>' \
  hologram serve \
    --advertise https://registry-b.example.com \
    --join https://registry-a.example.com

The joining node heartbeats immediately, learns the live membership set, and then heartbeats those peers directly. Failed seeds remain eligible for retry; members disappear from /api/v1/nodes after their TTL. Join messages use a short-lived keyed proof over the exact payload, so the cluster secret and the separate user authentication token are never sent over the wire. Non-loopback origins must use HTTPS.

The default configuration and local endpoint are:

~/.config/hologram/live.toml
http://127.0.0.1:11435

Open the endpoint for the built-in status page. API documentation is available at:

http://127.0.0.1:11435/docs
http://127.0.0.1:11435/openapi.json

/docs is the self-hosted Scalar reference; /openapi.json is the generated OpenAPI document. Native clients use the versioned Protobuf/gRPC service on the same endpoint.

A path no route claims is answered 404 with the daemon's error envelope (LIVE_NOT_FOUND). A caller that sends content-type: application/grpc gets gRPC UNIMPLEMENTED instead, as any gRPC server answers an unknown service.

Machine-readable CLI output

Global --json is supported by every CLI command and may appear before or after the subcommand. A successful command writes one JSON value to stdout, including lifecycle actions, downloads, generated files, accepted mutations, and run --output-format text. Diagnostics remain on stderr, while a runtime failure writes a JSON object with code and message to stdout and exits nonzero. This makes the complete CLI safe to compose with jq:

hologram status --json | jq -r '.status'
hologram files get blake3:... --output ./asset.bin --json | jq '.byte_length'
hologram run ./my-app --input-text 'hello' --output-format text --json | jq -r '.[]'
hologram run application.holo --output-format text --json | jq -r '.[]'

Help and shell-completion text retain Clap's human-readable format.

Demo workflows

Chat and conversation history

Create a thread, copy its returned ID, and send a message:

hologram --json history new "Demo chat"
hologram --json chat send <conversation-id> "Hello, Hologram"
hologram --json history show <conversation-id>

chat send records the user message and the assistant response as one persisted exchange. Threads retain separate histories and can be resumed from the desktop app.

The response comes from the inference engine selected in live.toml:

[inference]
engine = "echo"            # echo | weightc | ollama | llamacpp | candle | burn | vllm
default_model = ""         # imported id or remote served-model name
weightc_path = "weightc"
ollama_endpoint = "http://127.0.0.1:11434"
vllm_endpoint = "http://127.0.0.1:8000"
vllm_token_env = "VLLM_API_KEY"
model_path = ""            # GGUF (llamacpp/candle) or named-MPK checkpoint (burn)
tokenizer_path = ""        # tokenizer.json (candle) or tokenizer.model (burn)
model_architecture = ""    # candle: llama; burn: a supported Llama 3 variant
n_ctx = 4096
n_gpu_layers = 0
llamacpp_max_concurrent_requests = 1
request_timeout_secs = 300
resident_sessions = false  # weightc only: keep one resident enter session per conversation
max_resident_sessions = 4  # LRU cap on resident sessions

The default echo engine repeats the user message; it needs no model and no external process. The weightc engine shells out to weightc ask <artifact-dir> <prompt> --json against an imported .wcpu artifact. ollama proxies /api/generate, while vllm uses the OpenAI-compatible /v1/completions and /v1/models endpoints with native streaming and optional authentication from VLLM_API_KEY.

llamacpp loads a local GGUF model in-process and streams decoded pieces with exact token counts. Set model_path directly, or import a GGUF file and put its returned blake3:... id in default_model. It is off by default because it builds native C++ code and gives model execution the daemon's crash boundary. Build it with cargo build --release --features llamacpp; use llamacpp-metal or llamacpp-cuda for the corresponding GPU backend. These builds require CMake, Clang, and a C++ compiler.

candle is the Rust-native local GGUF alternative. The initial adapter supports Candle's quantized Llama-family implementation, requires a matching Hugging Face tokenizer.json, streams native deltas, and reports exact token counts. Set model_architecture = "llama"; use --features candle for CPU, candle-metal for Apple GPUs, or candle-cuda for NVIDIA GPUs. Candle is not a universal GGUF dispatcher: unsupported model families fail during startup instead of being guessed.

burn uses Tracel's Burn-LM Llama implementation on CPU. It requires a Burn named-MPK checkpoint, the matching Llama 3 tokenizer.model, and one of llama3.2-1b, llama3.2-3b, llama3.1-8b, or llama3-8b in model_architecture. Build with --features burn. Burn generation is currently buffered, so streaming API responses honestly report x-hologram-stream: emulated; GGUF files are not Burn checkpoints.

Run the live acceptance gate against real engines before releasing an inference build. It starts an isolated Hologram server and checks model discovery, buffered and native-streaming OpenAI/Ollama requests, usage, cancellation, and llama.cpp context overflow:

./scripts/check-inference-engine.sh llamacpp /models/tiny.gguf
HOLOGRAM_TOKENIZER_PATH=/models/tokenizer.json HOLOGRAM_MODEL_ARCHITECTURE=llama ./scripts/check-inference-engine.sh candle /models/tiny.gguf
HOLOGRAM_TOKENIZER_PATH=/models/tokenizer.model HOLOGRAM_MODEL_ARCHITECTURE=llama3.2-1b ./scripts/check-inference-engine.sh burn /models/llama3.2-1b.mpk
VLLM_ENDPOINT=http://127.0.0.1:8000 ./scripts/check-inference-engine.sh vllm org/model

VLLM_API_KEY is forwarded when set. Set HOLOGRAM_BIN to test an existing binary, or let the script build the required feature set.

With resident_sessions = true, the weightc engine instead keeps a supervised weightc enter --jsonl process per conversation, so turns reuse the live KV context instead of replaying a transcript, and only the new message crosses the wire each turn. Sessions are LRU-capped by max_resident_sessions; a crashed session is reported as a typed error and lazily respawned (starting fresh context) on the next turn. This mode needs a weightc build with enter --jsonl support. Models are managed with:

hologram models import ./tinyllama.wcpu
hologram models import ./tinyllama.gguf
hologram models list
hologram models remove blake3:...

Threads can be archived instead of deleted. Archived threads keep their messages and their updated_at_millis, but drop out of the default listing:

hologram --json history archive <conversation-id>
hologram --json history list          # archived threads are omitted
hologram --json history list --all    # archived threads included
hologram --json history unarchive <conversation-id>

In the desktop app, hovering a thread reveals an archive button, and archived threads collapse into an ARCHIVED group at the bottom of the thread list.

Configuration files use schema version 2, and a file written by an earlier build still starts. Missing sections and fields fall back to their documented defaults, and an older schema_version is upgraded and written back so the next start reads a current file; the rewrite records only what the file declares, never a value injected by an environment override. Unknown fields are still rejected rather than ignored, and a schema_version newer than the running build supports is refused rather than silently downgraded.

Removing a field from the schema comes with a schema_version bump and an entry in the retired-key table in src/config.rs. A file written before the bump keeps starting: the retired key is dropped during the upgrade, named in a warning, and gone from the rewritten file. Files already at the current version are not swept, so an unrecognised key there stays a startup error rather than being discarded as though it had been retired.

Files

hologram files put ./notes.txt --media-type text/plain
hologram files list
hologram files rename blake3:... meeting-notes.txt
hologram files get blake3:... --output ./meeting-notes.txt
hologram files search --filename-contains notes --limit 20

File bytes are addressed by their BLAKE3 content ID. Renaming changes only persisted filename metadata; the ID and bytes remain unchanged.

Object search

hologram registry search --kind file --limit 50
hologram registry search --media-type text/plain --min-size 1024
hologram --json registry search --limit 2 | jq -r '.next_cursor'
hologram registry search --limit 2 --cursor blake3:...

Search filters on stored metadata: --kind, --media-type, --filename-contains, --min-size, --max-size. It is not full-text and does not read object content.

Results are ordered ascending by object ID and paginated. --limit defaults to 100 and is clamped to 1000. A page carries next_cursor only while further matches remain; the cursor is opaque and valid only for the provider that issued it. truncated reports a page cut short by a provider-side scan bound, so a capped result is never presented as a complete one.

The same surface is available as GET /api/v1/objects/search and GET /api/v1/files/search, and as the registry.search and files.search native operations. hologram files search is the file-kind projection: the daemon fixes the kind, so --kind is not offered there.

Named artifacts

hologram push ./app.holo demo:v1
hologram serve demo:v1
hologram pull qwen3.5:4b
hologram pull host:5000/models/qwen3.5:4b
hologram run qwen3.5:4b --input-text "hello"
hologram --json pull qwen3.5:4b | jq '{archive_kappa, layers_fetched, bytes_transferred}'

A reference is [host[:port]/]namespace/name[:tag]. A bare name:tag expands against [registry].endpoint and [registry].namespace; an omitted tag means latest. Following OCI, the colon separates repository from tag, so qwen3.5:4b is repository qwen3.5, tag 4b.

An artifact is one OCI manifest whose layers are a thin .holo archive plus the kappa-addressed payload blobs it references, so two artifacts sharing weights transfer them once. Pull consults the local store first, fetches only what is missing, verifies every layer against its kappa on write, and confirms the whole set is present before reporting success — the registry itself does not check that a manifest's layers exist. Every step is content-addressed, so a pull is idempotent and an interrupted one resumes for free.

Tags are mutable, so a pull always re-resolves rather than caching by name, and records the resolved manifest digest — that digest, not the tag, is what makes a pull reproducible.

serve takes an optional reference. hologram serve alone is unchanged; hologram serve demo:v1 acquires the artifact, imports it, and makes it resident before the listener binds, so the daemon never reports ready with the named application not yet invocable. The argument joins whatever holo.resident already declares rather than replacing it.

That residency lasts for the life of that process. serve <ref> does not write to your configuration, so a daemon started any other way will not have it — put it in holo.resident if you want it every time.

run and serve resolve their argument in the same fixed order, so no existing invocation changes meaning:

  1. a blake3: id → the local catalog
  2. an existing filesystem path → a local archive
  3. anything else → a registry reference, pulled and then executed

Pulling grants no capabilities. A pulled archive receives the same ADR 020 baseline as a local file — no storage roots, no channels, no network scopes — and is executed by exactly the same path. Having come from a configured registry is not evidence about what an archive contains.

push is the inverse. The archive becomes the manifest's archive layer, and any payload a thin archive references without embedding is published alongside it — which is what lets a later pull deduplicate against blobs the registry already holds. A referenced payload that is not in the local store refuses the push before anything is written, rather than publishing a manifest whose content is missing. The archive is inspected first, so a file that is not a valid archive is rejected before the registry is touched.

Tags are mutable upstream, so push refuses to move a tag that already resolves unless given --force, and the result records whether a tag was moved. Silently repointing a name someone else is pulling is not a default worth having.

Progress is written to stderr and the result document to stdout, so --json stays usable in a pipeline; under --json the progress is JSONL events rather than a rendered bar.

Client library

Applications can talk to a daemon without depending on it. hologram-client is a standalone crate in this workspace:

use hologram_client::{HologramClient, ObjectQuery};

let client = HologramClient::new("http://127.0.0.1:11435")?;
let stored = client
    .put_file(b"hello".to_vec(), Some("notes.txt"), "text/plain")
    .await?;
let bytes = client.get_file(&stored.id).await?;

let page = client
    .search_files(&ObjectQuery {
        filename_contains: Some("notes".to_owned()),
        limit: 50,
        ..ObjectQuery::default()
    })
    .await?;

It depends on reqwest, rustls, serde, and serde_json only — taking hologram-live would pull wasmtime, axum, and tonic into every consumer's build. The cost of mirroring the wire shapes instead of importing them is drift, so a contract test in the daemon's suite serializes the server types and deserializes them with the client's; a renamed field fails a build rather than surfacing at runtime.

search_objects_all follows cursors to the end, stopping at a truncated page rather than looping, since truncation means the daemon stopped early.

.holo archives

Generate a validated source manifest interactively:

mkdir my-app
cd my-app
hologram app init

The generator prompts for ordered layers, their kind-specific entrypoint or surface information, the primary layer, an optional capability file, and optional child applications with delegated capability documents. It writes hologram.json atomically and prints the commands needed to compile and run it. For scripts and CI, provide the first layer as flags:

hologram app init ./my-app \
  --kind wasm \
  --path app.wasm \
  --entry holo_run

# Compose a previously compiled, self-contained child archive
hologram app init ./parent \
  --kind wasm --path parent.wasm --entry holo_run \
  --child worker.holo \
  --child-capabilities worker-capabilities.json

# A portable View source is a directory containing ui/index.html
hologram app init ./view-app \
  --kind view --path ui --surface portable

Use --yes for a minimal app.wasm/holo_run manifest. Existing manifests are preserved unless --force is explicit. Packaging remains a compiler choice: use hologram compile for a fat archive or add --thin for a manifest-only archive.

hologram holo fixture ./fixture.holo
hologram holo import ./fixture.holo
hologram holo list
hologram holo inspect ./application.holo
hologram holo plan ./application.holo
hologram holo verify ./application.holo
hologram holo inspect blake3:...
hologram holo plan blake3:...
hologram holo verify blake3:...
hologram holo remove blake3:...

Desktop watch loop

Open Applications in Hologram Desktop and choose Add directory, then select a project containing hologram.json. The desktop compiles it immediately, imports the successful archive into the normal local catalog, and recursively watches the project for later changes. Builds are debounced and written to the desktop cache rather than into the source directory.

Choose Run on a ready watched project to inspect its latest successful archive. Non-View applications accept a text input and execute once through the in-process executor. A portable View application instead offers Open application: it opens in its own native window outside the dashboard and keeps its prepared primary available across user messages until that window is closed, Stop application is chosen, or Desktop quits. Choose Add .holo to import an existing archive through the native file picker; catalog archives use the same Run panel. Unsupported providers and denied capabilities remain explicit runtime errors.

examples/wasm-view/ is the complete composed example. Its portable frontend posts one bounded application.invoke intent to an ASCII-uppercase Wasm primary:

hologram compile examples/wasm-view/hologram.json \
  --output target/wasm-view.holo

Import target/wasm-view.holo in Desktop and choose Open application. Its separate native window can submit any number of bounded messages to the same prepared Wasm primary. Closing the window or choosing Stop application detaches the View and stops the session in reverse layer order. The checked-in manifest is also compiled by display-independent lifecycle tests, which prove the persistent attachment, direct and View invocation, transactional window replacement, idempotent stop, and reverse shutdown without requiring a CI display. The CLI's hologram run remains deliberately one-shot and headless; it reports that no portable surface is available instead of changing its fresh-instance contract.

The Applications list is backed by the same holo list and holo inspect operations shown above, so its archive κ, application κ, layers, capabilities, physical sections, and verification state are not reconstructed by the web frontend. A failed rebuild is shown on the watched project while the last good immutable archive stays available. Stop watching removes only the persisted watch registration; it does not delete the last cataloged .holo archive.

The stable build creates and validates v4 .holo archives and rejects every other physical version. The physical file starts with HOLO, a version and section count, then fixed 24-byte section-table entries containing each section's kind, offset, and length. Logical layers do not have separate physical headers: their ordered descriptors live in the canonical AppManifest section and refer to payloads by κ. Every application archive contains exactly one verified application-directory extension.

.holo v4
├─ header + section table
├─ AppManifest       primary · requires · ordered layers · children
├─ Extension         verified, queryable application directory
├─ ContentBlob × N   κ71 · content bytes
├─ other sections    plans · weights · ports · certificates · metadata
└─ BLAKE3 footer

The closed layer kinds are wasm, tensor, rootfs, view, and v4's non-exit-bearing inference-model. A layer records its content κ and entrypoint plus an architecture for rootfs, surface for views, or engine identifier for model services. A View source names a directory containing index.html; the compiler emits canonical HOLOVIEW v1 bytes with portable paths and lexically ordered assets, independent of source timestamps, permissions, and creation order. Only the portable surface is accepted. Fat archives embed referenced blobs; thin archives retain the same application identity while resolving content through a store. Live emits fat archives by default, supports thin output with --thin, executes Wasm primary layers through wasmtime, and can directly execute a Python OCI bundle carried by a rootfs layer through the experimental local container provider. The portable View provider validates during prepare, attaches an available host surface during start, and detaches idempotently during reverse shutdown. Hologram Desktop publishes the concrete surface and serves immutable assets from an opaque per-attachment hologram-view origin bound to a separate native application window; it uses neither file://, source paths, nor a localhost server. Dynamic View windows are not included in the main window's Tauri capability assignment, block external navigation and popups, and receive a restrictive CSP. A View can send bounded v1 messages to its own admitted application by posting JSON shaped as {"version":1,"name":"application.invoke","payload":"text"} to the same-origin /_hologram/intent endpoint; the runtime serializes each application's direct and View invocations and returns UTF-8 outputs. Other names, versions, origins, content types, paths, and over-limit payloads fail closed, and no general Tauri or host API is exposed. Explicit application sessions keep providers and surfaces available across messages; one-shot execution still starts, invokes once, and stops. Stop waits for in-flight root and child application messages before reverse shutdown. CLI and server execution intentionally publish no surface, so planning reports an explicit direct/headless unavailable-surface blocker. Desktop window operations run through a display-independent lifecycle seam: replacement is transactional, a failed replacement restores the prior assets and window, detach failure restores the attachment for retry, and repeated detach is safe. The checked-in examples/wasm-view/ archive proves compilation, persistent intent invocation, explicit session ownership, and reverse shutdown.

Applications request authority with an optional capabilities.json. This is a human-authored compiler input, not the object embedded in the archive:

{
  "schema_version": 2,
  "storage_roots": [],
  "storage_quota_bytes": 0,
  "network_fetch_endpoints": [],
  "network_announce_endpoints": [],
  "publish_channels": [],
  "subscribe_channels": [],
  "memory_max_bytes": 0,
  "cpu_time_per_event_ms": 0,
  "priority_weight": 0
}

Endpoint entries use the exact form https://lowercase-host:explicit-port/path-prefix, must be strictly sorted, and narrow for children only on exact origin and path-segment boundaries. Schema-1 documents remain compatible when their legacy network booleans are absent or false; true is rejected rather than interpreted as ambient access. Scopes alone link no network interface. Only the explicit component-network-fetch@1 profile can use an admitted fetch scope; raw sockets and announce/listen interfaces remain unavailable.

Set "requires": "capabilities.json" in hologram.json. compile --check validates the version, fields, and canonical sorted κ and endpoint lists; compile converts the JSON into the upstream canonical CapabilitySet bytes and stores that object's κ in AppManifest.requires. Omitting requires produces the canonical empty request. A request describes what an application needs—it is never itself a grant. In the upstream capability contract, a scalar budget of 0 means unbounded; use a nonzero value to request a finite ceiling. Runtime grant enforcement now decodes this canonical request and authorizes it before any provider prepares. Schema-v4 source manifests may pair a self-contained child .holo archive with a delegated capability document; compilation binds both canonical κ values into the parent and embeds the verified child closure in a fat build. The planner verifies that recursive child closure—including every child manifest, delegated capability object, requested capability object, and layer—under one bounded κ-resolution walk. Before provider preparation, each delegation must be admitted by its parent's effective grant and must itself admit the child's request. Admitted children start depth-first in manifest order under their delegated grants and stop or roll back in exact reverse order. The current call invokes only the root primary; child primaries are lifecycle-managed dependencies rather than independent resident applications.

Capability objects always use canonical CapabilitySet bytes. The canonical empty set represents a deny-all request; zero-byte or otherwise malformed objects are rejected even when their content address is correct.

Ordinary local execution uses the built-in baseline grant: no storage roots, publish/subscribe channels, or network endpoint scopes. A non-empty request therefore fails with LIVE_AUTHORIZATION_DENIED. For an explicit local demo, provide a separate trusted capability source as the effective grant:

hologram --json run ./application.holo \
  --development-grant ./development-grant.json \
  --input ./payload.bin | jq

Resident execution reads its development grant only from the service's trusted configuration, never from a remote request. Set holo.development_grant = "development-grant.json"; relative paths resolve from paths.config_dir, and configuration validation rejects this mode on a non-loopback listener. Both direct and service modes emit a warning and trace the request κ, effective-grant κ, source, and allow/deny decision without logging the capability document. They also synchronously append each root request, child delegation, and child request decision to audit.jsonl under the configured state directory before provider preparation. Audit rows contain the authenticated principal, relation, application and parent identities, request and grant identities, trusted source label, and outcome—never tokens, source documents, roots, channels, or payload bytes. Successful raw run results expose the same non-secret decision metadata as requested_capabilities_kappa, effective_grant_kappa, grant_source, and authorization, so automated checks can retain the authority evidence:

hologram --json run ./application.holo \
  --development-grant ./development-grant.json |
  jq '{authorization, grant_source, requested_capabilities_kappa, effective_grant_kappa}'

See the complete .holo format guide for the byte layout, section kinds, manifest schema, identity model, application directory, verification rules, and current runtime support.

Archives whose primary layer is Wasm execute in-process through wasmtime. A self-contained archive can run directly without starting the service:

# Compile a source directory in memory and run it immediately
hologram run ./my-app --input-text 'hello' --output-format text

# Or compile once and run the resulting immutable archive
hologram compile ./my-app/hologram.json -o ./my-app.holo
hologram run ./my-app.holo --input ./payload.bin

Wasm layers use the import-free core-wasm-v1 contract. The module exports memory, holo_alloc(i32) -> i32, and the function named by the layer's manifest entry with signature (i32, i32) -> i64. holo_run is a generator default, not a runtime hard-code; an archive may declare another export such as transform. The packed i64 identifies one byte output. V1 has no WASI imports and no numeric process exit status: returning bytes is successful completion, while a trap is LIVE_PROTOCOL_ERROR. Direct and resident providers validate the declared entry during preparation and use a fresh instance for each input.

The callable entry stays separate from guest-contract selection. Source manifest schema v4 requires contract for Wasm layers; the compiler writes it to the canonical, identity-bearing aux tag. Empty or omitted contracts are rejected. In addition to core Wasm, the closed set contains import-free Component v1 plus the store-read, store-write, channel-publish, and channel-subscribe profiles described below. An import-free Component manifest uses hologram:guest/component@1:

{
  "schema_version": 4,
  "primary": 0,
  "layers": [{
    "kind": "wasm",
    "path": "app.component.wasm",
    "entry": "run",
    "contract": "hologram:guest/component@1"
  }]
}

hologram app init --contract hologram:guest/component@1 generates the same field. Component archives compile, inspect, plan, and execute directly or as a resident application; both JSON and gRPC report the normalized contract. The exact Component v1 entry is run. Its checked-in WIT world accepts and returns one byte list and imports nothing, so it cannot reach ambient WASI, filesystem, network, clocks, environment, or host services. Each input gets a fresh store while the compiled component remains warm. Runtime ceilings apply even when capability scalars are unspecified: 64 MiB memory, 100 million fuel units per input, 1 MiB input and output, and a two-second invocation deadline. Nonzero admitted memory or CPU-time limits can only tighten those ceilings. Deadline and dropped-future cancellation interrupt the isolated component engine. Guest errors, traps, and limit failures remain typed operation errors. There is never a fallback to core Wasm. Unknown source identifiers are rejected before archive emission, and unknown canonical manifest tags are invalid archives.

Components that need mediated object reads select the separate hologram:guest/component-store-read@1 contract and declare at least one exact object κ in storage_roots. That profile imports only hologram:host/store@1.0.0; its read function returns bytes for an admitted root and rejects every other target before accessing the store. The base component@1 world remains import-free. Direct execution uses the configured local registry, resident execution uses the catalog store, and child applications retain only roots admitted through their delegated grant. Missing profile authority returns LIVE_AUTHORIZATION_DENIED before linker construction. No filesystem, network, clock, environment, or other WASI interface is added by this profile.

Typed transitive reads use the distinct hologram:guest/component-store-graph-read@1 contract, so an existing exact root grant never widens implicitly. It exposes the same single hologram:host/store@1.0.0 import, but preparation resolves each admitted root through registered canonical UOR realization edges. Unknown or untagged bytes are opaque leaves. Every reachable object must already exist locally and re-hash to its κ; malformed typed frames, incomplete closures, and host depth, object, edge, or 64 MiB aggregate-byte limit violations fail before linker construction. The resolved read set is shared by direct and resident providers, while child applications may receive only root κ values admitted by their delegated grant. The write profile remains exact-root-only.

Components that need mediated object writes select hologram:guest/component-store-write@1. Its fixed world imports only hologram:host/store-write@1.0.0; it cannot read. The request and trusted grant must contain the exact destination κ in storage_roots and a nonzero storage_quota_bytes. Before any write, Live checks the exact root and proves the bytes hash to the caller-supplied κ. Newly materialized blobs consume the prepared application's lifetime quota, existing identical blobs consume no additional quota, and a rejected call leaves no partial blob. Direct, resident, and delegated-child paths use the same rules and expose no ambient WASI surface.

Components exchange bounded in-process messages through two separate profiles: hologram:guest/component-channel-publish@1 imports only hologram:host/channel-publish@1.0.0, while hologram:guest/component-channel-subscribe@1 imports only hologram:host/channel-subscribe@1.0.0. Preparation requires a nonempty exact request admitted by publish_channels or subscribe_channels, and every call checks the supplied channel κ. The runtime-owned broker provides nonblocking 64 KiB messages and 64-message FIFO mailboxes with explicit full-mailbox backpressure. One successful receive consumes one message; empty receive returns immediately. This v1 boundary is an in-memory, at-most-once work queue, not broadcast, replay, acknowledgement, persistence, or network transport.

Bounded outbound HTTPS GET uses the separate hologram:guest/component-network-fetch@1 profile. Its world imports only hologram:host/network-fetch@1.0.0, and preparation requires at least one admitted network_fetch_endpoints scope. Targets use the same canonical host/port/path grammar and are rechecked per call. Live resolves DNS under a public-address-only policy, pins the connection to checked addresses, disables redirects and environment proxies, and inherits no guest headers, credentials, cookies, or client identity. The host caps targets at 2 KiB, responses at 1 MiB, transport time at 1.5 seconds, and concurrent fetches at eight. Direct and resident execution share these rules; raw WASI networking and announce/listen remain unavailable.

hologram run accepts a project directory, its hologram.json, a local self-contained .holo file, or a catalog κ. Project references are compiled as fat archives in memory and are not written or imported. Repeat --input for binary file inputs or --input-text for UTF-8 values.

For warm, repeated execution, import and load the archive into a resident session:

hologram compile ./my-app/hologram.json -o ./my-app.holo
hologram holo import ./my-app.holo
hologram holo load blake3:...
hologram run blake3:... --input ./payload.bin
hologram holo resident
hologram holo unload blake3:...

The browser API exposes the same resident lifecycle at GET /api/v1/holo/resident, POST or DELETE /api/v1/holo/{kappa}/load, and POST /api/v1/holo/{kappa}/run. Run request inputs are JSON arrays of byte arrays. Native and HTTP resident records include requested_capabilities_kappa, effective_grant_kappa, grant_source, and authorization alongside lifecycle counters.

Resident sessions live in the service process: a restart drops them, and the catalog alone does not bring them back. To run an archive as a persistent service, declare it in live.toml and the daemon loads it into a resident session during every startup, before it reports ready:

[[holo.resident]]
kappa = "blake3:ad47a8206fa67ade51af75363b7677f50937c10e207f6a7394148c0db3f227af"

Each κ must name an archive already imported into the local catalog (blake3: plus 64 hex characters; duplicates are rejected at startup). Declared loads go through the same verified, audited capability admission as an explicit holo load, under the service's single effective grant and the local-runtime principal. A declaration that fails to load — an unknown κ or an under-granted archive — is logged with its reason and skipped without stopping the service or the other declarations; the failing archive is simply absent from GET /api/v1/holo/resident. Changes to [[holo.resident]] take effect on the next hologram restart.

The compiler emits fat archives by default. --thin emits the same canonical application manifest without its κ-addressed payloads:

hologram compile ./my-app/hologram.json -o ./my-app.holo
hologram compile ./my-app/hologram.json --thin -o ./my-app.thin.holo

With --json, compilation reports the three distinct identities directly:

hologram --json compile ./my-app/hologram.json -o ./my-app.holo |
  jq '{archive_kappa, archive_fingerprint, application_kappa, capabilities_kappa}'

archive_kappa is the BLAKE3 address of the complete physical file, archive_fingerprint is the footer integrity value, and application_kappa addresses the canonical AppManifest. Fat and thin files have different archive κ values and footer fingerprints but the same application κ and capabilities_kappa. The existing kappa returned by holo inspect, catalog, resident, and run operations continues to mean the physical archive object; inspection adds application_kappa when an application manifest is present.

Importing the fat archive verifies and caches its content blobs. A subsequently imported thin archive can resolve those payloads from the local κ store and use the same resident load/run commands. Direct file execution requires a fat archive because it deliberately has no external content resolver.

Compiled application archives include exactly one versioned application directory over their canonical manifest. hologram --json holo inspect ./application.holo inspects a local archive without importing it or starting the service; the command also accepts an imported blake3:... object ID. It exposes the physical kappa, canonical application_kappa, footer fingerprint, ordered layers, child applications, required capability set, model engine tags, and embedded κ-addressed blobs. The directory is re-derived and verified against the manifest and blob contents on import; missing, duplicate, malformed, or disagreeing directories are rejected. Structural v4 archives without an application manifest report no application κ and may omit the directory.

Use holo plan to explain whether an application can run before starting any provider. Local paths plan without the service; imported κ values plan against the local content cache. The payload-free report includes all three identities, fat/thin/hybrid packaging, the capability object, ordered layers, resolution sources, provider availability, planning limits, and stable typed blockers:

hologram --json holo plan ./application.holo |
  jq '{application_kappa, packaging, runnable, layers, blockers}'

hologram --json holo plan blake3:... |
  jq '{execution_target, resolved_object_count, resolved_bytes, runnable}'

An unsupported layer is still a successful plan: runnable is false and blockers explains the unavailable provider with a kind, error_code, and message. Malformed archives, missing catalog objects, and transport failures retain the normal typed JSON error contract.

AI model applications

Hologram Live now reads and writes .holo v4 InferenceModel layers. A complete model archive produced by hologram-ai can be imported and inspected without initializing its engine:

hologram --json ai inspect model.holo
hologram holo import model.holo

The inspection lists each callable service entry, its engine identifier, content κ, and whether the bundle is embedded. Model-source acquisition and R4G1 compilation remain owned by hologram-ai; this binary does not yet expose hologram ai compile or connect a model session to hologram ai infer. Attempting to execute a model-only archive returns LIVE_CAPABILITY_MISSING and identifies the archive as a library archive, because installing the inference provider would not make it runnable; provider availability remains visible in the plan report.

For low-level archive assembly, a source manifest can package an already-built provider bundle:

{
  "schema_version": 4,
  "library": true,
  "layers": [{
    "kind": "inference-model",
    "path": "model.bundle",
    "entry": "ai.default",
    "engine": "uor-r4"
  }]
}

A manifest without a primary layer, like this one, must declare "library": true; the compiler rejects library: true alongside a declared primary, and rejects an absent primary without the marker, both with LIVE_CONFIG_INVALID before any layer content is built. library is an optional boolean and defaults to false. Library archives fully support compile, import, inspect, and plan, and can be composed as a children entry in another application; only direct or resident execution of the root is refused, with LIVE_CAPABILITY_MISSING. Composing a View-only library as a children entry does not currently work, because a View layer can never be a primary and every application whose layer set contains a View layer is required to declare one; Wasm library children work today and are covered by a test.

weightc remains a chat execution provider over imported .wcpu directories. Those directories are not placed into .holo files until a deterministic single-blob bundle and validation contract is defined. See the AI model application guide and ADR 009.

Before direct execution or holo load starts a provider, Live builds a runtime-owned application plan from the canonical manifest. It recursively resolves and re-hashes root and child manifests, requested and delegated capability objects, and every layer from embedded content or the local κ store. Shared objects are deduplicated while logical applications remain distinct, and aggregate application-depth, application-count, layer, object, and byte limits bound the complete tree. Missing or malformed nested content and cyclic paths therefore fail before provider preparation. Runtime admission first admits the root request, then proves every delegated child grant is a subset of its parent's effective grant and admits the child's request. Each decision is written through the separate audit boundary with the real CLI or service principal before provider preparation; amplification or an under-granted request returns LIVE_AUTHORIZATION_DENIED. A closed LayerKind registry then prepares and starts the complete admitted tree depth-first in manifest order. Every child provider receives only that child's delegated grant. Direct and resident calls invoke only the root primary; child primaries are managed dependencies. Normal stop and failure rollback traverse the exact reverse order, and resident status aggregates root and child layers. Wasm layers use Wasmtime behind this boundary and may have a primary position other than zero; resident status reports state, aggregate resident bytes, queued calls, processed calls, and non-secret authorization evidence. Repeated load and unload are idempotent. Python rootfs archives use the same lifecycle through an explicitly experimental, direct-only OCI adapter. Inference-model services can be inspected but not invoked through Live yet. Tensors, inference models without a provider, resident Python rootfs archives, and unknown rootfs payloads return a typed LIVE_CAPABILITY_MISSING error. The compiler/runtime/executor boundary is recorded in specs/adrs/007-holo-compiler-runtime-execution.md and the planning/provider contract in specs/adrs/010-holo-application-plan-and-provider-lifecycle.md; the Wasm guest contract is documented in src/holo_wasm.rs and demonstrated by features/fixtures/wasm-app/.

Python applications

Python is a compiler input, not a fifth .holo layer kind. Both available profiles keep the same module:function entrypoint, where the function accepts and returns bytes:

  • schema-v4 wasi-component packages Python, CPython, and locked pure-Python wheels into an import-free WasmCodemodule selected by hologram:guest/component@1; it runs directly or resident without Docker;
  • schema-v2 rootfs packages Python, native dependencies, and Linux libraries into an architecture-specific OCI image for the experimental direct provider.

The dependency-free teaching example is examples/python-component-hello/:

$ hologram app init ./my-python-component \
    --template python --profile wasi-component \
    --project . --entry my_package:main --lock uv.lock
$ hologram compile examples/python-component-hello/hologram.json \
    --output python-component-hello.holo
$ hologram run python-component-hello.holo \
    --input-text Ada --output-format json
{
  "message": "Hello, Ada!",
  "name": "Ada",
  "python": "3.14.0",
  "runtime": "python-component"
}

This compiler invokes Hologram's deterministic componentize-py 0.25.0 distribution through an isolated uvx tool environment, removes the developer virtual environment and Python search path, and uses --stub-wasi. It selects one exact wheel URL and SHA-256 from the immutable componentizer-v0.25.0-hologram.5 release for each of the five server-release hosts, disables package indexes and source builds, and fails with LIVE_CAPABILITY_MISSING on an unpinned host. The emitted component therefore imports no WASI and runs under the existing Component v1 limits. Install uv; the first compile downloads the hash-verified wheel and later compiles may reuse its cache. For external packages, the portable profile accepts registry records only when uv.lock contains an HTTPS, SHA-256-pinned Python 3 *-none-any.whl. It installs those exact wheels into a private path with indexes, dependency re-resolution, source builds, and ambient pip/uv configuration disabled. Native, source-only, Git, and path packages fail during compile --check with guidance to use rootfs.

Both validation and compilation return a versioned, non-canonical provenance report when global --json is active:

$ hologram --json compile \
    examples/python-component-dependency/hologram.json --check \
    | jq '.build_provenance.layers[0].source \
      | {runtime, componentizer, inputs, dependencies, reproducibility}'

The schema records normalized SHA-256 source inputs, the complete selected dependency-wheel inventory, CPython and componentizer pins, the exact host-specific componentizer wheel URL/hash, immutable release and patch-set manifest identity, deterministic preinitialization contract, build host, and target ABI. A completed compile additionally records the observed uvx/uv versions and the generated layer κ and byte length. canonical: false is deliberate: the report is not embedded in .holo, so host evidence cannot silently change the canonical application identity. Save a durable copy with jq '.build_provenance' > application.provenance.json.

examples/python-component-dependency/ demonstrates six==1.17.0:

$ hologram compile examples/python-component-dependency/hologram.json \
    --output python-component-dependency.holo
$ hologram run python-component-dependency.holo \
    --input-text Ada --output-format json
{
  "dependency": "six-1.17.0",
  "message": "Hello, Ada!",
  "name": "Ada",
  "runtime": "python-component"
}

Stubbed randomness repeats deterministic build-time state inside an emitted component and is not suitable for security-sensitive runtime randomness. The pinned Hologram tool controls its private preinitialization randomness, clocks, filesystem metadata, directory enumeration, hash seed, allocator fills, and generated ordering. Completed output reports reproducible: true: workflow run 33227358037 proved matching component, application, archive, footer, and complete-file identities on two independent clean builders for every supported host. Offline compile --check remains false because it has not observed output. Real capability-gated WASI remains a later milestone.

Run the repeatable direct-and-resident proof with:

just python-component-holo-demo

Run two compiler invocations with separate Hologram and uv caches and query the single JSON result with jq:

just python-component-repro | jq '{status, target_host, equal, identities}'

The component-reproducibility workflow runs that proof on two independent clean runners for each supported macOS, Linux, and Windows host, then compares the component layer, capabilities, application, archive, footer, byte-length, and complete-file SHA-256 identities. Server releases depend on its aggregate result.

For the dependency-aware rootfs profile, source-manifest schema v4 turns a locked project into an architecture-specific rootfs layer containing CPython, the application, dependencies, and required Linux libraries.

For a small second application example, examples/python-hello/ uses only the Python standard library and runs directly from its project directory:

$ hologram run examples/python-hello --input-text Ada --output-format json
{
  "message": "Hello, Ada!",
  "name": "Ada",
  "runtime": "python"
}

The same project can be added as a watched directory in Desktop. After its Docker-backed build reaches Ready, choose Run and enter a name. Desktop verifies and retrieves the immutable catalog archive, then uses the direct executor so experimental Python rootfs applications do not depend on resident rootfs support.

The repository also includes the dependency-heavy NumPy + pandas project in examples/python-numpy-pandas/. A running Docker-compatible engine is required for both examples and this experimental compiler/direct executor:

$ hologram compile --check examples/python-numpy-pandas/hologram.json
$ hologram compile examples/python-numpy-pandas/hologram.json \
    --output numpy-pandas.holo
$ hologram run numpy-pandas.holo \
    --input examples/python-numpy-pandas/request.json \
    --output-format json
{
  "columns": [
    "label",
    "value"
  ],
  "mean": 20.0,
  "rows": 3,
  "sum": 60.0
}

Both validation and compilation expose non-canonical rootfs build evidence in JSON. Validation hashes the declared project inputs without contacting Docker:

hologram --json compile examples/python-numpy-pandas/hologram.json --check \
  | jq '.build_provenance.layers[0].source | {
      profile, target_platform, base_image, dependency_installer, inputs,
      reproducibility
    }'

A real compile resolves a mutable base tag through Docker's registry client, uses the resulting repository@sha256:digest reference in the actual FROM instruction, and records both requested and resolved references. It also adds observed Docker client/server versions and the emitted rootfs layer κ, exact image ID, and sizes. The report is deliberately canonical: false and is not embedded in the .holo file; save it separately when needed:

hologram --json compile examples/python-numpy-pandas/hologram.json \
  --output numpy-pandas.holo \
  | jq '.build_provenance' > numpy-pandas.provenance.json

Pass --no-build-cache to make Docker rebuild every generated rootfs layer. Completed provenance records builder.cache_disabled: true, so saved evidence can distinguish an uncached build from the normal developer path. Compare two uncached builds locally with one machine-readable report:

just python-rootfs-repro | jq .
just python-rootfs-repro | jq '.equal, .identities'

The release gate runs one uncached build on each of two independent clean Linux runners for both linux/amd64 and linux/arm64, then compares image, rootfs-layer, application, archive, and footer identities within each target. The architectures are intentionally not compared with each other because they are different application artifacts. macOS and Windows server binaries can drive a configured Docker-compatible Linux engine, but GitHub's hosted runners do not supply one; they are release hosts, not additional rootfs build platforms.

The first clean matrix completed all four builds with the same Docker 28.0.4 engine and pinned base but exposed target-local differences from BuildKit's cross-stage directory serialization. After the canonical runtime-tar change, workflow run 33035209550 passed with two matching clean replicas for each Linux target. The amd64 image ID is sha256:778bc5f5e4c66392b798ff8b6ad6178e42c80efff6a8ff44b18fbfc44573d31f; the arm64 image ID is sha256:1f55f44f41af891e3464b056f6b0beefbf6be9d736611de1505d17fb9a8cd754.

compile --check remains offline, so a mutable tag has no resolved_reference until a real compile. A digest-pinned source.base is already resolved and bypasses the registry lookup. Completed builds identify the normalized Docker archive, source epoch zero, bundle schema 3, and exact output and report reproducible: true. An offline check of a mutable base reports false with a blocker only until real compilation resolves and binds that base to an immutable digest; a digest-pinned check can report true immediately.

Authenticated private registries use Docker's existing credential boundary; Hologram has no registry username, password, or token option. The repository gate starts a disposable loopback registry, proves offline validation and anonymous rejection, logs in through an isolated DOCKER_CONFIG, then compiles and runs the standard-library Python archive. It rejects credential-bearing provenance fields and scans both reports and archive bytes for the fixture credentials:

just python-private-registry | jq .

The rootfs release workflow requires this proof before accepting its clean-builder comparison.

hologram run preserves the binary-safe HoloRunResult envelope by default. Add --output-format text for UTF-8 application output or --output-format json for JSON application output. One decoded result prints directly; results from multiple --input arguments print in order, with JSON results collected into an array. Invalid text or JSON returns a typed protocol error instead of changing the bytes.

The raw envelope keeps output and completion distinct. Core-Wasm v1 returns "completion":{"kind":"returned"} because its callable returned bytes but has no process exit code. A provider with a real process status may return "completion":{"kind":"exited","code":0}. Completion and authorization evidence are required on every current result. Traps, nonzero processes, and other failures remain typed errors rather than successful results with invented status values.

Generate the same schema without hand-writing JSON:

hologram app init ./my-python-app \
  --template python --profile rootfs \
  --project . --entry my_package:main --lock uv.lock --arch arm64

Compilation stages only pyproject.toml, the declared uv.lock, and src/, then installs the locked third-party dependencies in a disposable Linux builder. It does not read or copy the host .venv, and it does not install the local project as a wheel because uv's local-project cache metadata contains source timestamps. The builder normalizes the application and launcher to epoch zero and writes them as one lexically sorted GNU tar. Hologram copies that artifact from a stopped builder container and constructs the final digest-bound image with local ADD, avoiding engine-selected cross-stage directory ordering without executing a foreign-architecture process. The runtime imports the staged application through /app/src; uv and transient build layers are excluded. Direct execution validates the archive and target architecture, disables networking, mounts the container filesystem read-only, drops Linux capabilities, enables no-new-privileges, and applies CPU, memory, PID, temporary-storage, input/output, and 30-second wall-clock limits.

This is an intentionally explicit demo provider, not the final untrusted-workload boundary: it requires a local Docker-compatible engine with Buildx registry inspection, supports direct fat archives only, and leaves cached OCI images behind for repeat runs. Bundle schema 3 re-addresses the exact image config and ordered layers as SHA-256 blobs, emits a canonical manifest and fixed tar headers, then applies fixed-level Zstandard compression. New archives record the exact image ID, so a warm local run skips decompression and docker image load only when that trusted ID is already present; a cold machine still restores the image from the archive. Compile once with an optimized release binary and reuse the resulting .holo; debug builds spend substantially longer hashing the roughly 100 MiB archive. just python-holo-demo builds and uses the release CLI, with a one-time optimized link on the first invocation. Mutable source.base tags are resolved and bound to a registry manifest digest before the build; an already digest-pinned value skips that lookup. Clean uncached builder equality is proven for both supported Linux target architectures. Capability-gated WASI and hardware-backed microVM rootfs execution remain planned. See the Python application guide, ADR 008, ADR 012, ADR 014, ADR 015, and ADR 017.

The demo writes one JSON document to stdout; progress and failures use stderr:

just python-holo-demo | jq .
just python-holo-demo | jq '.output'
just python-hello-demo | jq .

# Retain the verified archive instead of deleting the temporary artifact
just python-holo-package target/numpy-pandas.holo | jq '{archive, archive_bytes}'
target/release/hologram --json run target/numpy-pandas.holo \
  --input examples/python-numpy-pandas/request.json

HoloTrade quote engine

examples/holotrade-quote/ is a worked port of HoloTrade's deterministic single-node pricing engine to the core-wasm-v1 guest contract. One zero-dependency Rust file — built with plain rustc, no Cargo project — takes a self-contained JSON quote request and returns the full price decomposition P = P_base × E × G × D × H × Q × L, including the operating floor, margin, carbon, and the locality multiplier computed from the W(3,3) symplectic geometry inside the guest. The port is differentially checked against HoloTrade's own JS PricingEngine on ten scenarios to 1e-9 relative tolerance.

rustup target add wasm32-unknown-unknown
cd examples/holotrade-quote
rustc --target wasm32-unknown-unknown -O -C panic=abort \
  --crate-type cdylib guest.rs -o app.wasm

hologram compile hologram.json -o holotrade-quote.holo
hologram run holotrade-quote.holo --input request.json --output-format text

It also makes a good persistent service: import the archive, declare its κ under [[holo.resident]] in live.toml, and every hologram serve boot loads it into a resident session, invocable at POST /api/v1/holo/{kappa}/run. See the example README for the request schema, the ported-source inventory, and the fidelity notes.

Inference compatibility APIs

The daemon exposes OpenAI- and Ollama-compatible HTTP surfaces over the configured inference engine, including streaming:

curl http://127.0.0.1:11435/v1/models
curl -X POST http://127.0.0.1:11435/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{"model":"echo","messages":[{"role":"user","content":"Hello"}]}'
curl -X POST http://127.0.0.1:11435/api/generate \
  -H 'content-type: application/json' \
  -d '{"model":"echo","prompt":"Hello","stream":false}'
curl http://127.0.0.1:11435/api/tags

stream: true is accepted by every engine. Streaming request:

curl -N -X POST http://127.0.0.1:11435/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{"model":"echo","messages":[{"role":"user","content":"Hello"}],"stream":true}'

Every response — streaming or not — carries an x-hologram-stream header of native or emulated. native means the engine produced deltas as it generated them (currently ollama); emulated means the engine has no incremental output, so the daemon ran the completion to finish and replayed the result as a single delta (currently echo and weightc). The tokens are identical either way — emulation reconstructs only the arrival schedule, never the content — so on an emulated engine, time-to-first-token equals time-to-full-completion. See specs/adrs/022-streaming-and-token-usage.md for the full reasoning, including how token usage is reported (omitted, not zeroed, when an engine did not measure it).

Both surfaces sit behind the same bearer-token seam as every other module route.

Third-party plugin modules

Beyond the built-in catalogue, the daemon hosts dynamic third-party modules as supervised subprocesses. Plugins are an explicit, sha256-pinned allowlist in live.toml — nothing is scanned or loaded implicitly:

[plugins]
enabled = true

[[plugins.modules]]
id = "com.example.weather"
path = "/usr/local/bin/weather-plugin"
sha256 = "<64 hex chars>"

Each plugin speaks a small gRPC contract (hologram.live.plugin.v1.PluginHost: describe/invoke/ping) over a Unix socket the daemon provides, runs under a supervised actor with a bounded mailbox and bounded restart, and receives a scrubbed environment. Its operations join the capability manifest and are reachable through the native client:

hologram plugins list
hologram plugins call com.example.weather weather.current '{"city":"Berlin"}'

Plugins have no host resource access in v1 — no store, config, or network mediation — and no HTTP routes; they are pure compute over JSON payloads. Wrapping plugin executables in microVMs is the documented hardening path. See specs/adrs/005-subprocess-plugin-boundary.md.

Built-in modules

Trusted modules are statically linked and registered in the builtin_modules! catalogue in src/modules/mod.rs:

ModuleCurrent responsibility
dev.hologram.live.systemHealth, capabilities, and module discovery
dev.hologram.live.kappa-registryContent-addressed registry provider (local or external Kappa Registry), with bounded metadata search
dev.hologram.live.filesFile upload, listing, search, renaming, and download
dev.hologram.live.holo.holo import, inspection, verification, cataloguing, and resident Wasm execution
dev.hologram.live.historyDurable conversations and messages
dev.hologram.live.chatConversation-backed chat over the configured inference engine
dev.hologram.live.inferenceModel import, listing, and removal for the engine boundary
dev.hologram.live.openai-compatOpenAI-compatible /v1 inference API, including streaming
dev.hologram.live.ollama-compatOllama-compatible /api inference API, including streaming
dev.hologram.live.control-planeMinimal node inventory and heartbeats

Each module declares its stable ID, dependencies, operation IDs, HTTP routes, and OpenAPI contribution. Operators can enable a subset with modules.enabled in live.toml. Executable module behavior remains compiled Rust rather than configuration-defined code.

Configuration

The current configuration schema is version 2. Generate a complete file with hologram init, inspect it with hologram config show, and validate the installation with