Get started

Quickstart

You'll build a small Rust program whose core refuses to run without a Sigil license. Then you'll issue a license in the portal, activate it, and watch the install appear. The steps are the same ones you'll use on your real codebase.

You need: a conFIEL Auth account for the portal, Rust 1.85 or newer (rustup update), and curl and tar. This quickstart builds a native program. Protecting a .wasm works the same way, and Protect a WASM module covers the differences.

Sign in to the portal

Open sigil.enigmahq.sh/portal and choose Sign in with conFIEL Auth. On your first sign-in Sigil creates your account, named after your organisation, with the seat pool your conFIEL entitlement includes. Every license you issue draws machines from that pool.

Register your application

Go to Applications → Register an application and give it a name, for example conFIEL Engine. Sigil generates an Ed25519 signing key for it. Copy the application id (app_…) from the application's page.

Create a policy

A policy is a tier: what a license unlocks, where it may run, and how long it works offline. On the application's page, under New policy:

FieldValueWhy
NameEngineShown when you issue licenses.
EntitlementscosignThe feature name your code will require.
PlatformsServerNative programs activate as server.
Machines per license1Seats each license takes from your pool.
Lease / grace14 / 7 daysHow long an install keeps working offline.

Download the SDK

The Rust SDK is a tarball for now; it will move to GitHub releases. Check it against the published SHA-256 and unpack it next to where your project will live:

Terminalconsole
$ curl -fLO https://sigil.enigmahq.sh/downloads/sigil-sdk-rust-0.2.0.tar.gz
$ echo "01d38edffdcfe0138b50fe8d66d243b6704164d2910938f1cb12a0a19dbc24a8  sigil-sdk-rust-0.2.0.tar.gz" | shasum -a 256 -c
sigil-sdk-rust-0.2.0.tar.gz: OK
$ tar xzf sigil-sdk-rust-0.2.0.tar.gz

This gives you sigil-sdk-rust-0.2.0/ containing lease/ (the verifier), client/ (activation for native hosts), examples and the JavaScript host SDK. Download lists everything in it.

Add Sigil to your project

Create the program (or open your own) beside the SDK and add the two crates as path dependencies:

Terminalconsole
$ cargo new my-engine
$ cd my-engine
my-engine/Cargo.tomltoml
[dependencies]
sigil-lease = { path = "../sigil-sdk-rust-0.2.0/lease", features = ["std"] }
sigil-client = { path = "../sigil-sdk-rust-0.2.0/client" }

Embed your application's keyset

Your binary must trust your application's public keys, and only those. Download the keyset with the button on the application's page in the portal, or fetch it. It's public, so no sign-in is needed:

Terminal (in my-engine/)console
$ curl -fsS https://sigil.enigmahq.sh/v1/client/apps/app_YOUR_APP_ID/keys -o sigil-keys.json

Commit it with your code, and download it again for every release. It holds your active key and your standby key, so a build you ship today keeps working when you rotate. After a rotation, download it again so your next build carries the new standby.

Gate the protected code

Replace src/main.rs with the program below. The two lines that matter are include_bytes!("../sigil-keys.json"), which fixes trust at build time, and gate.require_now(FEATURE), which refuses unless a valid lease for this machine grants cosign. The rest is a small CLI around sigil-client.

my-engine/src/main.rsrust
//! A native program whose core only runs with a Sigil license.
//!
//!   my-engine activate SGL-XXXXX-…   take a seat on this machine, cache the lease
//!   my-engine run [message]          the protected work: refuses without a license
//!   my-engine status                 what the gate sees, verified offline
//!   my-engine deactivate             release the seat
//!
//! Environment: SIGIL_API (default https://sigil.enigmahq.sh), SIGIL_STATE
//! (default ./.sigil).

use std::process::ExitCode;

use sigil_client::{Client, Config};
use sigil_lease::{Gate, Keyset};

// Downloaded from the portal (or GET /v1/client/apps/<app>/keys) and compiled
// in. This is where trust is fixed: the gate accepts leases signed by these
// keys and no others, whatever the host or the network says at run time.
static KEYS: &[u8] = include_bytes!("../sigil-keys.json");

// The entitlement the protected work needs: one of the policy's entitlements.
const FEATURE: &str = "cosign";

fn main() -> ExitCode {
    let keyset = Keyset::parse(KEYS).expect("sigil-keys.json is a Sigil keyset");
    let api = std::env::var("SIGIL_API").unwrap_or_else(|_| "https://sigil.enigmahq.sh".into());
    let state = std::env::var("SIGIL_STATE").unwrap_or_else(|_| ".sigil".into());
    let client = Client::new(Config::new(api, keyset.app(), state).app_version(env!("CARGO_PKG_VERSION")));
    let mut gate = Gate::new(keyset);

    let args: Vec<String> = std::env::args().skip(1).collect();
    let result = match args.first().map(String::as_str) {
        Some("activate") => match args.get(1) {
            Some(key) => client.activate(key).map(|s| {
                let l = s.license.expect("an online activation describes its license");
                println!("✔ activated: {} · {} · seat {}/{}", l.name, l.entitlements.join(", "), l.machines_used, l.max_machines);
            }),
            None => {
                eprintln!("usage: activate <license-key>");
                return ExitCode::from(2);
            }
        },
        Some("run") => client.unlock(&mut gate).and_then(|_| {
            gate.require_now(FEATURE)?; // the check: inside this binary, against the keys above
            let message = args.get(1).map(String::as_str).unwrap_or("hello");
            println!("✔ running · {}", protected_work(message));
            Ok(())
        }),
        Some("status") => client.unlock(&mut gate).map(|u| {
            let c = &u.verified.claims;
            println!("✔ {:?} · licensed to {} · {} · lease until {}", u.verified.status, c.lic, c.ent.join(", "), c.exp);
        }),
        Some("deactivate") => client.deactivate(Some(&mut gate)).map(|_| println!("✔ seat released")),
        Some("fingerprint") => client.fingerprint().map(|fp| println!("{fp}")),
        _ => {
            eprintln!("commands: activate <key> | run [message] | status | deactivate | fingerprint");
            return ExitCode::from(2);
        }
    };
    match result {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("✖ not licensed: {e}");
            ExitCode::from(3)
        }
    }
}

/// Stands in for the code worth protecting.
fn protected_work(message: &str) -> String {
    let digest = message.bytes().fold(0xcbf2_9ce4_8422_2325u64, |h, b| (h ^ b as u64).wrapping_mul(0x0100_0000_01b3));
    format!("co-signed \"{message}\" → {digest:016x}")
}

In your own codebase, put the require call at the top of each protected entry point, with the feature that entry point needs.

Run it without a license

Terminalconsole
$ cargo run -q -- run
✖ not licensed: NOT_ACTIVATED: no license on this machine: activate a license key first

The program builds and starts, but the protected work refuses. The same happens to anyone who copies the binary: without a lease signed for their machine, require fails.

Testing against a local Sigil? The program talks to https://sigil.enigmahq.sh by default. Set SIGIL_API=http://localhost:8787 to point it at a development server. Leases are cached in ./.sigil; set SIGIL_STATE to move them.

Issue a license

In the portal, open Licenses → Issue license. Pick your application and the Engine policy, and name the licensee. Copy the key from the dialog. It's shown once, and Sigil stores only its hash.

License keytext
SGL-7KQ2M-4WZ9P-R3T8X-B6N1C-H4X9D

Activate and run

Terminalconsole
$ cargo run -q -- activate SGL-7KQ2M-4WZ9P-R3T8X-B6N1C-H4X9D
✔ activated: conFIEL Engine (prod) · cosign · seat 1/1
$ cargo run -q -- run "invoice 42"
✔ running · co-signed "invoice 42" → 7f3e1c0d2b9a4e61
$ cargo run -q -- status
✔ Valid · licensed to conFIEL Engine (prod) · cosign · lease until 1791234567

Activation sent the license key and this machine's fingerprint to Sigil, took one seat, and cached a lease. From then on run works offline. The client renews the lease once it's past half its life, whenever the network is up.

See it in the portal

Open Installs. Your machine is listed as live on platform Server, with its hostname, version and lease expiry. The license's page shows the seat it holds, and Activity records Machine activated. From here you can release the seat or revoke the license. A revoked license stops at the install's next renewal, and the cached lease is dropped.

Next