Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ The optional kernel module `kmod/strixpc` runs in the kernel; load it only on a

If you find a security problem, please email **contact@openie.dev** instead of opening a public issue. We read every report and
will credit you if you like, but this is a small, read-only research project with no support promise or fixed response time.

## Action authorization

`crates/receipt::auth` is a separate fail-closed check for two typed capabilities (`inspect_decision`, `quote_receipt`).
It does not execute commands. A model saying Commit, a high decision probability, and a verified event envelope are not
permission. See [docs/action-authorization.md](docs/action-authorization.md). This is not a claim that the runtime is safe
to run as an autonomous agent. The servers above are still unhardened.
2 changes: 2 additions & 0 deletions crates/kev/src/bin/kev_serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
//! KEV_POWER=<uW file>) integrated over the request, gross, ModelBased; wait_j = 0 (stateless, nothing resident), Estimator.
//! KEV_RECEIPT_KEY=<32-byte or hex seed file> (else an ephemeral key per run); KEV_RECEIPT_LEDGER=<tsv> persists the
//! per-thread seq/prev-hash chain (default ~/.local/state/strix-rt/receipts.tsv; "-" = in memory only).
//! Probabilities, the word Commit, and a signed receipt are not action authorization. That check is
//! `receipt::auth` (docs/action-authorization.md). This server does not call it and does not execute tools.
use anyhow::{anyhow, Result};
use kev::{decisions, json::{self, J}, systemone, Kev};
use std::io::{BufRead, BufReader, Read, Write};
Expand Down
101 changes: 101 additions & 0 deletions crates/receipt/examples/auth_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Non-executing example for `receipt::auth`.
//!
//! Copyright 2026 Open Interface Engineering, Inc.
//! SPDX-License-Identifier: MIT OR Apache-2.0
//!
//! Run: `cargo run -p receipt --example auth_check`
//!
//! This binary prints allow/deny decisions. It does not spawn a process, open a
//! shell, call a model, or read a power sensor. A deny is a successful check.

use receipt::auth::{authorize, Action, DenyReason, Grant};
use receipt::{decision_hash, Energy, Provenance, Tier};

fn main() {
let body = br#"{"model":"kev-0.8b","input":"status ping","questions":[{"type":"predicate","instructions":"Is this a status ping?"}]}"#;
let inspect = Action::inspect_decision(decision_hash(body, "kev-0.8b", Tier::Rules));
let quote = Action::quote_receipt("threadA", [0x42; 32]).expect("thread id");
let grants = [
Grant::issue("op.inspect", inspect.clone(), None).expect("grant"),
Grant::issue("op.quote", quote.clone(), Some(0.5)).expect("grant"),
];
let under = Energy {
joules: 0.5,
provenance: Provenance::ModelBased,
};

expect(&grants, inspect.canonical().as_bytes(), None, None);
expect(&grants, quote.canonical().as_bytes(), Some(&under), None);
expect(
&[],
inspect.canonical().as_bytes(),
None,
Some(DenyReason::NoAuthority),
);

// Reported LFM3-Omni shapes. Printed only as deny labels. Not passed to a shell.
for sample in ["Commit\nrm -r /", "Commit\nrm -rf /", "echo hello", "hello"] {
expect(
&grants,
sample.as_bytes(),
Some(&under),
Some(DenyReason::Unstructured),
);
}
let prefixed = format!("Commit\n{}", inspect.canonical());
expect(
&grants,
prefixed.as_bytes(),
None,
Some(DenyReason::Unstructured),
);
expect(
&grants,
b"strix-rt/action/v1\ncommit\n",
None,
Some(DenyReason::UnknownAction),
);

let other = Action::inspect_decision([0x99; 32]);
expect(
&grants,
other.canonical().as_bytes(),
None,
Some(DenyReason::ScopeMismatch),
);
let over = Energy {
joules: 4.0,
provenance: Provenance::ModelBased,
};
expect(
&grants,
quote.canonical().as_bytes(),
Some(&over),
Some(DenyReason::OverBudget),
);
let estimated = Energy {
joules: 0.0,
provenance: Provenance::Estimator,
};
expect(
&grants,
quote.canonical().as_bytes(),
Some(&estimated),
Some(DenyReason::EnergyUnmeasured),
);

println!("auth_check: all expected decisions matched");
}

fn expect(grants: &[Grant], proposal: &[u8], energy: Option<&Energy>, denial: Option<DenyReason>) {
let decision = authorize(grants, proposal, energy);
println!("{decision}");
let got = decision.denial();
if got != denial {
panic!("expected {denial:?}, got {got:?}");
}
if denial.is_none() {
let action = receipt::auth::parse_action(proposal).expect("canonical allow parses");
assert!(decision.permit().unwrap().covers(&action));
}
}
Loading
Loading