Chio/Docs
LOGIN · JOIN

Delegate Sensitive Research

Delegate a review of work that read a customer's contract terms, keep the result useful to your team, and keep the terms off the public pull request.

Guide chio@5f63e6e

On this page

The fixer needs Harbor Goods’ negotiated terms to fix issue #417, where quote() drops the tier discount for sku-204. Delegate the review to reviewer, return the review to your pricing team, and post exactly one summary that your pricing lead approved to the public pull request. The terms’ restrictions stay on everything the workflow learned.

Starts from chio new security-research --example security-research, a host for the pricing project with Harbor Goods’ terms and issue #417 in its documents.

The host in the security-research project runs the kernel’s native flow lifecycle against durable SQLite state and two independently journaled HTTP receivers. It is a trusted local operator program: it holds the fixer and reviewer identities, signed tool manifests, a classifier, and the pricing lead’s disclosure key, performs file reads and loopback sends, and runs no language model or confined worker. To expose the same operations to an agent, connect an authenticated process host.

Start
Delegated reviewChoose an operation.
reads termsdelegatesrequestsreadssendsrefusedsends oncebindssignsaa₁τKρfd₁d₂fixerreviewerdisclosure grantreceiptdocuments/internal/publicretained labellarkfield-pricing · harbor-goods-termsaa₁τKρfd₁d₂fixerreviewerdisclosure grantreceiptdocuments/internal/publicretained labellarkfield-pricing · harbor-goods-terms

One disclosure grant admits one exact send

The pricing lead’s grant binds this summary and the public destination for ten minutes. Dispatch consumes it under public-reviewed-v1, and the retained label still carries the contract restrictions. Step 6.

The host’s five operations on the pricing project: the fixer and its reviewer, the kernel, the documents, two receivers, and the pricing lead’s disclosure grant. The keys, the SQLite stores, and the classifier are left out.

The workflow separates three decisions. Reading the terms gives the fixer a customer’s confidential pricing, delegating gives the reviewer narrower permission to work with it, and posting to the pull request changes the audience to anyone who can read a public repository, so it needs its own disclosure decision.

1. Build the host and start its receiver

Before you begin

Use macOS or Linux, Rust 1.94.1 or newer, Python 3.11 or newer, jq, and the installed chio CLI. The host/ subdirectory has its own manifest and lockfile; the portable label programs at the archive root run on their own.

Terminalbash
chio new security-research --example security-research
cd security-research/host
cargo build --locked --bin research

The host depends on the published chio-kernel, chio-flow, chio-process, and chio-store-sqlite crates; Cargo checks their versions against the lockfile. The result is a separate research binary beside the installed chio. If the build fails, check rustc --version and the project’s lockfile before changing source.

Terminal 1bash
python3 receiver.py .receiver

The receiver binds 127.0.0.1:19041 and logs /internal, the pricing team’s channel, and /public, the public pull request, to separate JSONL files: two destination policies in one local service. Give it a fresh directory and a free port; the receiver counts in later steps assume empty journals. Open a second terminal in security-research/host/ and put the built command on its path, repeating this in any new terminal:

Terminal 2bash
export PATH="$PWD/target/debug:$PATH"

2. Read Harbor Goods’ terms as the fixer

Terminal 2bash
research init
research read terms \
  --as fixer --operation terms-v1
Output
Allow: terms-v1
Caller: fixer
Document: terms

CONTRACT-HARBOR-GOODS-17
Harbor Goods pays 18 percent below list on sku-204 through June, with a floor of USD 41.00. Do not quote these terms outside Larkfield.

You should see the terms: 18 percent below list on sku-204, with a USD 41.00 floor. Initialization issued the fixer capability and a signed, attenuated reviewer capability, retained the keys and manifests, and initialized the native flow authority. Before releasing the text, the kernel recorded the larkfield-pricing owner and the harbor-goods-terms compartment in principal, lineage, and session state.

3. Continue the work as the narrower reviewer

Terminal 2bash
research read issue \
  --as reviewer --operation review-source-v1
Output
Allow: review-source-v1
Caller: reviewer
Document: issue

Issue #417: quote() drops the tier discount for sku-204 when one order spans two price lists.

You should see issue #417 returned to the reviewer. Its delegation depth is 1: the grant removes permission to delegate, halves each per-tool invocation limit from 64 to 32, and expires after 30 minutes instead of the fixer’s hour. The host registers the reviewer’s fixed budget share when it reopens. The public issue adds context, and the shared lineage keeps the terms’ restrictions.

Each command reopens the same stores in a new host process. Actor names select retained grants in this operator interface; an untrusted worker never receives these keys and never chooses its own principal, lineage, or isolation epoch.

4. Return the review to the pricing team

Save the reviewer’s summary as summary.txt, then send it to the internal destination:

summary.txttext
quote() applies the tier discount for sku-204 across both price lists; a new test covers the split order.
Terminal 2bash
research publish summary.txt \
  --as reviewer --to internal \
  --operation internal-review-v1
Output
Allow: internal-review-v1
Caller: reviewer
Destination: internal

You should see the internal destination acknowledged, and the internal receiver logs the exact summary with its digest. Two conditions admitted the send: the grant permits invoking the publisher, and the publisher’s clearance preserves the contract label. Tool permission alone never decides where previously learned information may go.

5. Post the same summary to the pull request and see it refused

Terminal 2bash
research publish summary.txt \
  --as reviewer --to public \
  --operation public-unreviewed-v1
Output
Deny: public-unreviewed-v1
Caller: reviewer

You should see a deny for public-unreviewed-v1, and the command exits 2. The native flow boundary refused before the public connector sent anything, so no public journal exists; that receiver observation is separate from the signed decision. The summary names no customer, and the refusal stands anyway: it was written by a session that read the terms. Rewriting it without the contract marker, or reading more public material, leaves the accumulated label in place.

6. Approve one summary and publish it

Your pricing lead reads the final contents of summary.txt and approves exactly those bytes for the pull request. The host signs the grant with the lead’s approver key and prints the path where it retains the grant.

Terminal 2bash
research approve summary.txt \
  --as reviewer --operation public-reviewed-v1
Output
Approved exact summary: public-reviewed-v1
Grant: .research/operations/public-reviewed-v1/disclosure.json
Terminal 2bash
research publish summary.txt \
  --as reviewer --to public \
  --operation public-reviewed-v1 \
  --grant .research/operations/public-reviewed-v1/disclosure.json
Output
Allow: public-reviewed-v1
Caller: reviewer
Destination: public

You should see the public destination acknowledged. The pricing lead’s grant binds this summary and this destination for ten minutes, and native dispatch consumes the use: the public receiver observes one summary while retained state stays restricted. A later post needs its own compatible clearance or grant. Declassification, the embedder API behind this grant, continues with exact replay, changed drafts, and refused reuse.

7. Verify the receipt and the receiver journals

Terminal 2bash
chio receipt verify \
  --input .research/operations/public-reviewed-v1/completed.receipt.json \
  --trusted-kernel-pubkey .research/kernel.pub
Output
Verified 1 receipt signatures, signer pins and action hashes.

You should see one receipt verified. The verifier checks the receipt’s signature, signer pin, and action hash against the separately retained kernel public key, which establishes what the kernel signed. The receiver journal separately establishes which summary reached the pull request.

Inspect the release, the labels, the delegation, and the journals

These commands read the original read and the completed post; reopening the operation sends no second summary. JSON mode retains the complete application record, and jq selects the fields discussed here.

Terminal 2bash
research release public-reviewed-v1 --json > release.json
jq '{state, raw_recorded, evaluated, schema: .release.schema}' release.json
research read issue --as reviewer --operation review-source-v1 --json > child.json
jq '{depth: .receipt.metadata.attribution.delegation_depth,
     label: .flow.snapshot.lineage_label}' child.json
research inspect --as reviewer --json > flow.json
jq .snapshot.session_label flow.json
cat .receiver/internal.jsonl
cat .receiver/public.jsonl

A completed result carries the raw invocation record, the post-return evaluation, and the original release checkpoint. Output release explains their order and compares a lost reply with an interrupted effect; Persistent taint follows new sessions and reopened state; Classification changes the detector input without lowering the source floor.

Stop the receiver with Ctrl-C. Keep the state directory while you examine receipts or replay the original operation. Its retained grants expire; after expiry, start a fresh exercise instead of replacing keys or clearing stores inside an old operation.

Your own host passes when it reproduces five observations

A deployed host resolves labels from authenticated context and durable state, never from a label the model supplies or a fresh empty label per call. The reviewer gets its own authenticated principal and an attenuated capability, carries the fixer’s lineage restrictions when it receives the material, and returns to an internal receiver whose clearance retains the resulting obligations.

A shared invocation limit bounds the family’s total activity. The host carries the signed flow declaration and the complete authenticated context through its adapter, and durably joins the classified review output before releasing it.

ExerciseObserve at the hostObserve outside the host
Internal review succeedsBound child request and permitted releaseThe internal result arrives
Public post is refusedRetained label and a signed denyNo public request arrives
A public input is read laterThe join keeps prior obligationsPublic post stays refused
Host restarts after the terms readPrincipal and lineage state reopensThe next disclosure still obeys it
Output release is interruptedOriginal operation and release checkpointNo duplicate tool effect on replay

The deny’s flow reason sits in receipt guard evidence on an embedder kernel and in the host’s log on the native process host. Keep three observations independent: the host’s admission and guarded-return decisions, the destination’s record of what arrived, and the restrictions a reopened state authority returns. A correct in-memory label establishes none of them. When a row fails, change one boundary at a time:

  • The internal result is refused. Inspect reader sets and compartments before adding tool authority.
  • A public request reaches its receiver despite a deny. Inspect alternate network and host-tool paths.
  • A restart loses the restriction. Repair persistence and release ordering before extending the workflow.

Portable label programs

The project root holds the unchanged portable Chio label and lattice modules. Its programs isolate the policy calculations behind the steps above; they authenticate no reviewer, persist no native state, and contact no receiver. If your own label fails construction, inspect owner self-membership, canonical identifiers, and collection bounds; if egress fails, compare owners, readers, and compartments in the label reference.

Download and run the lab

Use the Rust version declared in the project’s Cargo.toml. The project includes a lockfile, the example, and two unmodified Chio source modules with their provenance and license; its local manifests select the portable label and lattice APIs, with no second implementation of the security decision. Pull the project, run the program, and run its tests:

Terminalbash
chio new security-research --example security-research
cd security-research
cargo run --locked --quiet
cargo test --locked
Output
ALLOW pricing-team destination: clearance retains the contract restrictions
DENY public destination: source label does not flow to destination clearance
RETAIN later public input: session restrictions unchanged
DENY missing clearance: egress clearance is missing
DENY Top clearance: top is not an operational egress clearance
NARROW shared owner: readers intersect to larkfield-pricing and fixer
DENY Larkfield-only destination: Harbor Goods obligations also apply
RETAIN delegated input: Larkfield and Harbor Goods obligations remain

Each line prints after an assertion checks its decision; the run checks nine assertions. Six tests cover prior-read retention, unknown sources, independent owners, shared-owner intersection, compartment loss, and order-independent joins, including two counterexamples: an unknown source cannot egress even toward Top, and your pricing team’s terms joined with Harbor Goods’ own NDA obligation cannot reach a destination that accepts only your team’s. Run cargo test --locked after changing a reader set or compartment to see which property your change affects.

Execute the parent, child, and return joins

One inherited restriction from your pricing team (the larkfield-pricing owner), the reviewer’s read of a document Harbor Goods owns, and the result returning to the fixer. Two independent owners make a dropped obligation fail visibly.

security-research/examples/delegated_restrictions.rsrust
use chio_flow::{authorize_egress, EgressDenial};
use chio_security_types::{InformationLabel, PrincipalId};
use std::collections::{BTreeMap, BTreeSet};

fn owned(name: &str) -> InformationLabel {
    let owner = PrincipalId::new(name).unwrap();
    InformationLabel::try_known(
        BTreeMap::from([(owner.clone(), BTreeSet::from([owner]))]),
        BTreeSet::new(),
    )
    .unwrap()
}

fn main() {
    let parent_knowledge = owned("larkfield-pricing");
    let child_read = owned("harbor-goods");
    let child_knowledge = parent_knowledge.join_restrictions(&child_read).unwrap();
    let returned_knowledge = parent_knowledge
        .join_restrictions(&child_knowledge)
        .unwrap();

    assert_eq!(returned_knowledge, child_knowledge);
    assert_eq!(
        authorize_egress(&returned_knowledge, Some(&parent_knowledge)),
        Err(EgressDenial::FlowViolation),
    );
    assert_eq!(
        authorize_egress(&returned_knowledge, Some(&child_knowledge)),
        Ok(())
    );
    println!("Child input and returned result retain both Larkfield and Harbor Goods owners");
}
Terminalbash
cargo run --locked --quiet --example delegated_restrictions
Output
Child input and returned result retain both Larkfield and Harbor Goods owners

The fixer’s original destination, which accepts only your team’s obligation, no longer satisfies the combined result, and a destination retaining both owners does. The program models the label calculations at delegation and return; the host’s delegation, persistence, and guarded return produce the same expected labels with an authenticated reviewer.

Give the terms an owner

src/main.rsrust
fn contract_label() -> InformationLabel {
    let owner = PrincipalId::new("larkfield-pricing").expect("canonical owner");
    let fixer = PrincipalId::new("fixer").expect("canonical reader");
    InformationLabel::try_known(
        BTreeMap::from([(owner.clone(), BTreeSet::from([owner, fixer]))]),
        BTreeSet::from([Compartment::new("harbor-goods-terms").expect("canonical compartment")]),
    )
    .expect("bounded label")
}

The larkfield-pricing owner permits itself and the fixer, and the harbor-goods-terms compartment keeps one customer’s terms apart from every other customer’s. The owner appears in its own reader set because a known label requires it.

Join the read into the session

src/main.rs · in mainrust
let public = InformationLabel::bottom();
let internal = contract_label();
let session = public.join_restrictions(&internal).expect("valid join");

assert_eq!(authorize_egress(&session, Some(&internal)), Ok(()));
assert_eq!(
    authorize_egress(&session, Some(&public)),
    Err(EgressDenial::FlowViolation),
);

The internal destination accepts every restriction; the public destination drops them and is refused. Joining another public input into session leaves the label equal to the restricted session, because the latest message changes nothing the agent already learned.

Combine a second owner without losing the first

src/main.rs · in mainrust
let customer = label_for("harbor-goods", &["harbor-goods", "fixer"], &["harbor-goods-nda"]);
let combined = session
    .join_restrictions(&customer)
    .expect("independent owner join");
assert_eq!(
    authorize_egress(&combined, Some(&internal)),
    Err(EgressDenial::FlowViolation)
);
assert_eq!(authorize_egress(&combined, Some(&combined)), Ok(()));

Harbor Goods owns its NDA, permits itself and the fixer, and requires the harbor-goods-nda compartment. The join keeps both owners and both compartments, so a destination that accepts only your team’s obligation can no longer receive the result. label_for, in the example’s src/main.rs, builds canonical principals and compartments and calls InformationLabel::try_known; selecting and authenticating the destination stays with the host.

The executable also joins two policies from the same owner, your pricing team, one permitting the reviewer and one not, and their intersection removes the reviewer. Shared owners narrow their reader sets; independent owners keep separate obligations.

Next: replay the release without a second send