Skip to content

Releases: action-state-group/capsule-cli

capsulectl v0.1.0-rc15

Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Oct 19:40
72577c0

Prebuilt capsulectl binaries (linux/amd64, linux/arm64, darwin/arm64). Verify with: sha256sum -c SHA256SUMS. Check the build attestation with no GitHub account: gh attestation verify FILE --bundle capsulectl-v0.1.0-rc15.sigstore.json --repo action-state-group/capsule-cli --signer-workflow action-state-group/capsule-cli/.github/workflows/release.yml

capsulectl v0.1.0-rc14

Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Oct 17:29
49651e2

capsulectl v0.1.0-rc14, from commit 49651e2.

Disclosure first. A shared copy no longer names the per-profile payee companion record: it is still listed as a withheld step, so the copy stays complete, but under a neutral kind. The user's floor and spending limit are now recognised in any written money form (symbols, codes, locale separators, k, a bare number after a money word), compared by value. A shared copy that states the floor, or a counterparty copy that states the limit, is refused. A message the agent writes, and when the user sells an agent's claim, is refused before sealing if it states either. The user's own words are recorded as said.

Fails closed. When a seller's floor is in force but its opening can't be built, the deal check is refused before anything is sealed, instead of running without it.

Wire changes and release pairing. New deal records seal action taxonomy version "6", and every deal check now sends party_role at the top level of the rules checker's input. A rules checker compares the taxonomy version exactly, and a checker built before this release refuses an input carrying party_role, so this release needs a rules checker that holds taxonomy 6 and accepts party_role. Records sealed before re-derive unchanged.

Changes

A shared copy lists the counterparty_profile step under a neutral kind

  • Changed: a shared copy (counterparty or adjudicator) lists every step of the log, withheld
    ones included, so a verifier can tell withheld from missing. The check's counterparty_profile
    companion was listed by that kind, which tells the reader the user keeps a fingerprint of
    merchants across deals. It is now listed as kind private, still withheld, with the line "A
    record kept for the user only (withheld)".
  • Unchanged: the record itself, and the user's own copy, which names it as it is.

The user's floor and limit are recognised in any written form

  • Fixed: the check that keeps a seller's floor (min_total_minor) and a spending limit
    (max_total_minor) out of a shared copy only matched the form capsulectl writes ("$1700.00").
    It now reads money as prose writes it, and compares by value:

    • currency symbols before or after the amount ($, €, £, ¥ and others), ISO codes
      (USD 1,700, 1700 usd, also written against the number: USD1,700, 1700usd) and
      currency words (dollars, euros);
    • thousands separators as locales use them (comma, dot, space, apostrophe, no-break or thin
      space), with or without cents (1.700,00 €, 1'700.00 CHF), and k ($1.7k);
    • a bare number a few words after a money word ("the lowest I'll take is 1700").

    Dates, times, ids and digests are not read as money. An amount the other party is shown anyway
    (the asked price, an offer, a payment) is still not protected.

  • Added: a message the agent writes (from: agent), and, where the user sells, an agent's
    claim (which the buyer's copy opens), is refused before it is sealed when it states the floor or
    the limit in any of these forms. The refusal names the field, never the value. The user's own
    words are recorded as said.

A floor in force goes to the rules checker opened, or the check is refused

  • Fixed (fail closed). A rules checker reads a floor only from its verified
    commercial_bounds_opening, and given none it finds the floor rule not applicable. When a floor
    was in force but its opening could not be built, the checker was run without it. Now:
    • deal check refuses before sealing anything, naming the cause and never the floor. The causes
      are: the deal's limits cannot be read, no opening on this device recomputes to the commitment,
      or the opening is not a commercial-bounds document.
    • A floor is in force when the limits in force set one, whatever can be recomputed from its
      opening.
    • With no floor in force, nothing changes.
  • Fixed: typed deals sent the wrong opening. In a typed deal, the opening sent was the open
    step's, but a checker verifies it against the task authority it is given, which seals the floor
    under its own nonce. The two never matched, so a checker verifying the opening failed every such
    check. The task authority's own opening is now sent.

New deal records seal action taxonomy version 6

  • Changed (wire; records sealed before re-derive unchanged). A deal record's
    taxonomy_version is now "6": capsule-engine's capsule_engine/guards/action_taxonomy.json
    at 7ae6b95b37a3a1c4955af90f56ae4c1c0c404aa9 (file sha256
    7563fcaeb6c192a27877fae3131bca1021526046c8976475c46eebf4fb374272, JCS sha256
    fc12eb90bc2b3c85bb49c4d3d7be8f384c6e0b8e189bc3b68a753a4f1af5cd94).
  • Every action_class is unchanged. Against version 5 (e5ec2c1), version 6 changes only
    approver_role (to account_holder) on agreement.accept, marketplace.offer and
    marketplace.sale. No class is renamed, removed or added, and none changes trigger class.
    • A seller's steps carry: an offer external_commitment.other, a commit (accepting a buyer)
      agreement.accept, and telling the buyer where the item is disclosure.personal. A rental's
      or service's commit or sign is also agreement.accept, whose meaning is unchanged.
    • Deal records never carry marketplace.offer or marketplace.sale.
  • Each step keeps its own version. A record sealed under "2", "3", "4" or "5" re-derives
    with it.
  • Release pairing. A rules checker compares taxonomy_version exactly, so a record sealing
    "6" needs a checker that holds version 6. The next release from main ships with such a
    checker; none is cut from main before one is available.

A rules checker is told which side of the deal the user is on

  • Changed (checker input). Every deal check now sends party_role (buyer or seller) at the
    top level of external-check-input/v0. It is the side the deal's opening intent sealed, or
    buyer when it names none, and it is always explicit, so a checker selects its rules without
    inferring the side.
  • Release pairing. The input's top level refuses members it does not list, so a checker built
    before this change refuses every check that carries party_role. A release that sends it ships
    with a checker that accepts it.
  • The deal id has one source per record: an x-deal-v0 record's deal_id, or a typed record's
    chain_id, which is exactly that value. No record carries both, and a test now holds it.

A typed deal's history acts carry the profile payee key

  • Fixed (checker input only; nothing sealed changes). A history act in a deal recorded with typed
    records now carries counterparty_profile, resolved from the check its authorization rests on,
    whether that act needed an approval or not. Before, it was resolved only through an approval, so a
    repeated payment in a typed deal was not seen as a repeat by a rules checker. Untyped deals are
    unchanged.

Verify

Prebuilt binaries: linux/amd64, linux/arm64, darwin/arm64.

V=v0.1.0-rc14; R=action-state-group/capsule-cli
gh release download "$V" --repo "$R"
sha256sum -c SHA256SUMS
for f in capsulectl-"$V"-*-*; do
  gh attestation verify "$f" --bundle "capsulectl-$V.sigstore.json" --repo "$R" \
    --signer-workflow "$R/.github/workflows/release.yml"
done

The deal skill is installed from this tag's source archive; its skills/deal/SKILL.md equals the tagged commit's.

capsulectl v0.1.0-rc13

Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Oct 11:18
7eb05ac

capsulectl v0.1.0-rc13, from commit 7eb05ac.

Disclosure first. Deals where the user sells are new in this release. A seller's floor is never sealed in the clear and no shared copy carries it as a sealed field; the buyer's copy shows the offer, the task authority, the acceptance and the seller's agent's representations, and per-thread salted commitments keep two buyers' copies of one sale from being linked by any digest or commitment; both still show the item's name and the offer, the seller's own listing. A buyer's deal shares the same records with the same audiences as before. A new per-profile payee fingerprint is sealed only for the user's own copy, on a companion record that every shared copy withholds. The check that keeps the floor out of written text still misses some formats; a fix follows.

Wire changes (all additive; records sealed before re-derive unchanged). New deal records seal action taxonomy version "5". A refund's check carries reverses_ref. A check naming a payee is followed by a counterparty_profile companion record, and the rules checker's input gains optional counterparty_profile, item_ref and commercial_bounds_opening members.

Changes

Where the user sells: a seller profile, an exact acceptance, and one item for several buyers

  • Added (wire, additive; every deal sealed before re-derives unchanged). party_role
    (buyer | seller) on the intent, set when a deal opens and fixed; absent means buyer.
    deliver_by and perform_by obligations with a due_by date. A not_selected close.
  • A seller's floor stays private. min_total_minor is never sealed. Records that state it
    seal bounds_commitment, a salted commitment (sha256-jcs-nonce256) to a
    commercial-bounds/v0 document. Only the user's own copy carries its openings, and only the
    user's own rules checker is given the opening in force (commercial_bounds_opening). No
    shared copy, for any audience, carries the floor (see "Not yet"). A check below the floor asks
    (under_floor).
  • An exact acceptance. offer is a check, allowed only where the user sells; a later offer
    supersedes the earlier one (supersedes). deal note --kind acceptance seals the buyer's
    acceptance of the exact offer by its digest. A seller's commit needs an acceptance of the
    latest offer with no change since, must equal that offer's amount, currency, terms and
    recourse, and cites the acceptance. A deal where the user sells must use typed records.
  • A sale is money in. A seller's commit and offer count 0 toward any spend total.
  • What the buyer's copy shows (seller deals only). The offer, the task authority in full,
    the acceptance, and the seller's agent's own representations to the buyer (claims, with an
    optional class: condition, warranty, refund_terms, delivery_promise, other) whose
    words hold none of the user's private details. A buyer's deal is shared exactly as before.
  • One item, several buyers. deal sale new seals a sale's own log; deal open --sale ID
    opens a buyer's thread under it. Each thread seals the item reference and the sale's task
    authority only as per-thread salted commitments, so no digest or commitment links two buyers'
    copies; both still show the item's name and the offer, which are the seller's own listing.
    The user's own rules checker is given the plain item_ref.
  • Not yet: the check that keeps the floor out of written text misses some formats
    ("$1,700.00", bare amounts); a fix follows.

The payee keyed per profile, for the user's own history

  • Added (wire, additive; records sealed before re-derive unchanged). A check that names a
    payee is now followed, before any rules checker runs, by a counterparty_profile record.
    • What it holds: only {"fp_alg": "hmac-sha256-profile-key", "ids": {"payee": …}}, the
      payee fingerprinted with the same construction as the per-deal fingerprints. Its key is
      derived from the profile store's own secret, so one merchant has one value across the
      profile's deals and another in any other profile.
    • Where it points: it names its check with an about ref. Each deal record's per-deal
      counterparty fingerprints are unchanged.
  • Never shared: no shared copy, for the counterparty or an adjudicator, carries it; the check
    itself stays shareable.
  • The rules checker gets it: external-check-input/v0 gives the checker
    record.counterparty_profile for the check being decided, and history[i].counterparty_profile
    for each earlier act (from its approval's check). It is supplied beside the sealed capsule and
    never part of it. Acts checked before this release carry none.

New deal records seal action taxonomy version 5

  • Changed (wire; records sealed before re-derive unchanged). A deal record's
    taxonomy_version is now "5": capsule-engine's capsule_engine/guards/action_taxonomy.json
    at e5ec2c1ba84f9d9269cbb164eb6a92400e4d5d49 (file sha256
    7d31e892cdea7352d0485c89430dba028bf02ce91fb0e932f4752c1f4066022f, JCS sha256
    af3a054cae880e13ce5d8d8bdaa64383e6f3af14fa2bf2ddb88aab226865b13a).
  • Every action_class is unchanged. Against version 4 (5300e0b), version 5 changes only
    approver_role (to account_holder) on communication.send, a class no deal record carries;
    no class is renamed, removed or added, and none changes trigger class.
  • Each step keeps its own version. A record sealed under "2", "3" or "4" re-derives with
    it.

A refund's check names the payment it returns

  • Added (wire, additive; records sealed before re-derive unchanged). A deal check of a
    cancel that returns a sealed payment (a refund) now carries reverses_ref: that payment's
    action, by its record digest, as a typed reference ({"type": "record", "digest_alg": "SHA-256", "digest": …}). It names the same payment, by the same digest, as the refund
    action's reverses ref. A typed deal's proposed-action/v0 carries it too.
  • Amounts unchanged: a refund's check and action both keep amount_minor. A partial
    cancel's keep cancelled_amount_minor and name no payment. No returned_minor is sealed.

deal checkpoint status: a deal log's latest checkpoint, read-only

  • Added: capsulectl deal checkpoint status --deal ID returns, as JSON:

    • the deal log's latest checkpoint: its id (the SHA-256 of the signed statement), at, and
      the log head it covers (mmr_size, entries, root);
    • the log's log_entries;
    • the checkpoint's witness state as stored.

    A deal with no checkpoint yet has checkpoint and witness null, and exits 0.

  • Read-only: it never seals, never cuts a checkpoint and never contacts the witness, and every
    file under the store is byte-for-byte the same after it. It works on a read_only profile.

  • Fixed: opening a log that does not exist could panic while cleaning up the failed open. It
    now returns the log's error.

Verify

Prebuilt binaries: linux/amd64, linux/arm64, darwin/arm64.

V=v0.1.0-rc13; R=action-state-group/capsule-cli
gh release download "$V" --repo "$R"
sha256sum -c SHA256SUMS
for f in capsulectl-"$V"-*-*; do
  gh attestation verify "$f" --bundle "capsulectl-$V.sigstore.json" --repo "$R" \
    --signer-workflow "$R/.github/workflows/release.yml"
done

The deal skill is installed from this tag's source archive; its skills/deal/SKILL.md equals the tagged commit's.

capsulectl v0.1.0-rc12

Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Oct 07:39
ba436db

capsulectl v0.1.0-rc12, from commit ba436db.

Disclosure first. A deal claim's words are now sealed as a commitment, and only whose claim it is (source_kind) shows; a counterparty's copy no longer carries a claim's text, and an adjudicator's copy opens only claims that hold none of the user's private details. Records sealed before this release are unchanged and still verify.

Wire change. New deal records seal action taxonomy version "4". Records sealed under "2" or "3" keep their stored value and re-derive byte for byte. A rules checker that compares the taxonomy version must know version 4.

Changes

A deal claim's words are sealed as a commitment; only whose it is shows

  • Changed (wire, additive; records sealed before re-derive unchanged). A claim, on a claim
    step and in the baseline's claims[], is now sealed as:

    • text_commitment: the claim's words, salted, like every other text a deal record carries;
    • source_kind: whose it is (merchant, agent, platform, user or external), in the
      clear;
    • optional source_ref_commitment: the free-form note of where it was read, committed.

    A verdict names the claims it found unverified by reference (unverified_claims), not by their
    words. The typed action-evaluation/v0 does the same.

  • source_kind is stated by the caller. A source that can only mean the other party
    (counterparty, seller_message, merchant_email) is taken as merchant. Any other source (a
    page, a photo, a snapshot) must state it, and a claim that leaves it open is refused, naming the
    field. The deal skill says how to choose.

  • Openings:

    • the user's own report carries claim_openings;
    • an adjudicator's copy carries those of claims holding none of the user's private details;
    • a counterparty's copy shows each disclosed claim record (its source_kind and salted
      commitments) and no opening.
  • The profile checker: check_profile.py --openings=FILE recomputes each opening against the
    sealed record. A shared vector (claim words and source note) is checked by both the Go and the
    Python implementation.

doctor --check-witness is ok only when the witness answers with success

  • Fixed: the witness probe sent a HEAD to the endpoint and called any answer ok. A witness that
    serves no HEAD routes answered 405 and was reported reachable: true, status_code: 405, ok: true,
    while the first delivery could still fail.
  • Now: the probe is a read-only GET of <endpoint>/health, the witness's health endpoint. It is
    ok only on a 2xx answer. Any 4xx or 5xx is ok: false, with an issue naming the status, the
    method and the URL.
  • Changed: a delivery the witness answered with an error keeps the witness's HTTP status (never
    its response body), and its pending text says it: "pending: the witness answered with an error
    (HTTP 409); it is retried at every tick."

New deal records seal action taxonomy version 4

  • Changed (wire; records sealed before re-derive unchanged). A deal record's
    taxonomy_version is now "4": capsule-engine's capsule_engine/guards/action_taxonomy.json
    at 5300e0b.
    A reader that holds a later version found no class-keyed result for a record that named "2".
  • Every action_class is unchanged. Versions 2, 3 and 4 name the same classes. Versions 3
    and 4 add approver_role account_holder:
    • version 3 to money.purchase, money.subscription, booking.create and booking.modify;
    • version 4 to booking.cancel, data.delete, communication.publish and
      disclosure.personal.
  • Each step keeps its own version. A record sealed under "2" or "3" re-derives with it.

A check carries the inputs a rules checker reads

  • Added (wire, additive): a check sealed from this version carries scalars a rules checker
    reads. They go on the x-deal-v0 check and the typed proposed-action/v0, and each is absent
    when not known:

    • recipient_role (fulfilling_merchant or third_party, on a share);
    • channel and first_contact_channel (channel kinds);
    • upfront_amount_minor (the stated deposit, never inferred);
    • material_fields_changed and offer_fields_changed (counts over two fixed field lists: against
      what was agreed, and against the keys the user's own words state, absent when they state none),
      with material_fields_basis and offer_fields_basis (each list's digest);
    • on typed records, task_authority_ref.

    None carries a name, a contact detail or free text. Records sealed before re-derive unchanged.

  • Added: a typed deal's task-authority/v0 also carries its plan: outcome_id (a fixed id per
    deal type, capsulectl.deal.<purchase|rental|booking|service>/1.0.0), its allowed actions as
    allowed_actions, and preconditions: []. The external-check input gains an optional task_authority_record
    member: that whole sealed record, whose SHA-256 over its JCS bytes is the check's
    task_authority_ref digest; its plan is at body.

  • The deal skill now says to always state recourse (the rail and whether it is refundable) on
    a pay, and to put a deposit the checkout asks for in terms.

Rules a checker declares but does not measure are said quietly, not as a warning

  • Fixed: a rules checker reports the rules its ruleset declares but it does not measure as
    not_evaluable with the reason declared, not measured. On every escalated purchase, the deal
    card listed each one as a reason to ask ("Your rules ask for approval: …: declared, not
    measured"); under an overall not_evaluable it would read "not fully checked".
  • Now: such a finding is never a difference.
    • The approval text names the rules quietly beside the verdict: "3 rules are declared by the
      ruleset but not measured by this checker (r14, r24, r25)."
    • So does the user's own receipt, on the check's line. A shared copy does not name them.
    • The verdict is unchanged: an allowed purchase stays allowed.
    • Any other not_evaluable is still a difference, said as before.
  • Presentation goldens: a new fixture, 7-receipt-declared-rules.

Verify

Prebuilt binaries: linux/amd64, linux/arm64, darwin/arm64.

V=v0.1.0-rc12; R=action-state-group/capsule-cli
gh release download "$V" --repo "$R"
sha256sum -c SHA256SUMS
for f in capsulectl-"$V"-*-*; do
  gh attestation verify "$f" --bundle "capsulectl-$V.sigstore.json" --repo "$R" \
    --signer-workflow "$R/.github/workflows/release.yml"
done

The deal skill is installed from this tag's source archive; its skills/deal/SKILL.md equals the tagged commit's.

capsulectl v0.1.0-rc11

Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Oct 21:24
2579fa8

Prebuilt capsulectl binaries (linux/amd64, linux/arm64, darwin/arm64). Verify with: sha256sum -c SHA256SUMS. Check the build attestation with no GitHub account: gh attestation verify FILE --bundle capsulectl-v0.1.0-rc11.sigstore.json --repo action-state-group/capsule-cli --signer-workflow action-state-group/capsule-cli/.github/workflows/release.yml

capsulectl v0.1.0-rc10

Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Oct 08:12
e3f4656

capsulectl v0.1.0-rc10

Behaviour and wire changes

A deal receipt's text is sealed with it

Before: a deal receipt's readable text (the summary lines, each step's line, the amounts, what was told and the anomalies)
was carried in the bundle's x-deal-v0 extension, which no record seals. Editing that text left verify --bundle VALID, and the
receipt page showed the edited text.

Now: writing a receipt seals that copy's own text as a record. That covers deal report --html, --bundle and --email,
bundle --deal and disclose --deal.

Wire change: one sealed report record per receipt written.

  • The record: an fyi capsule on the deal's own log (action_id: "capsulectl-deal-report"), not chained to the steps.
  • Its input: {"type": "deal_report", "profile": "x-deal-v0", "deal_id", "audience", "nonce", "report"}. The nonce is a fresh
    random 256-bit value, so the record's digest confirms no guess at the text.
  • The bundle: its x-deal-v0 extension is now {"sealed_report": "<capsule id>"}. The text is that record's disclosed input,
    checked like every other disclosure.
  • One record per receipt: each copy seals its own text. A shared copy seals the text already rewritten for its audience, never
    the user's own.
  • Later copies: they carry every earlier report's record with its input withheld. A copy discloses its own text and no other
    copy's.
  • No file, no record: a deal report that writes no file seals nothing.

What verify --bundle reports for x-deal-v0:

The bundle Result
Names its sealed report, whose disclosed text matches pass
Text edited, or the extension names another record INVALID, sealed_report_unverified
Holds a sealed report record but the extension names none: removed, or replaced with text, even with the record's input dropped INVALID, sealed_report_not_named
A receipt written before this release (no report record; the text in the extension) VALID as before: uninterpreted, with the finding extension_unbound and a note that its text is not verified

The page:

  • A sealed copy says it checked that the text has not changed since it was sealed, and that the text is capsulectl's summary of
    the steps, not the steps themselves.
  • An edited or downgraded copy shows no deal text.
  • An older receipt rendered with this release says its text is not checked.

The deal's log now holds report records after its steps:

  • Reading a deal sets them aside.
  • Witness coverage is counted in steps: a tick whose checkpoint holds every step, with only report records after it, still reads as
    witnessed, or as pending for its stated reason.

Records declare their commitments' construction (commit_alg)

Wire change (additive). Every deal record sealed from this release carries commit_alg: "sha256-jcs-nonce256": in the
x-deal-v0 block, or in a typed record's header. It names the construction of the record's *_commitment values:

  • SHA-256 over the RFC 8785 (JCS) bytes of {"nonce", "text"};
  • a fresh 256-bit random nonce for each commitment, and no key;
  • recomputable only by a holder of the opening (the nonce and the text).

Older records and shared copies: records sealed before carry none and re-derive unchanged. commit_alg is on the share
allow-list, so shared copies still disclose the records that carry it. PROFILE.md section 4 states the construction, its nonce and
key scope, and where openings travel (only the user's own copy carries any).

disclose seals each disclosure as a capsule

Wire change: what disclose puts on the log. It used to append the bare digest of a disclosure record that was kept nowhere
else. Once a checkpoint covered such a digest, every later bundle and disclose on that profile failed ("operation failed …
sensitive details suppressed").

  • Now: it seals the disclosure record as an ordinary fyi capsule (action_id: "capsulectl-disclosure") with the profile's
    signing key, stores it, and appends its capsule id.
  • The record's input: ids, digests and labels only, never disclosed payload bytes, plus a random 256-bit nonce.
  • A later bundle proves the earlier disclosure's place in the log but withholds its input (payloads_mode: selected), so a copy
    made for one party never shows what was disclosed to another.

A page is written only when it can stand behind what it shows

bundle --html, disclose --html and deal report --html write a page only for a bundle that verify --bundle calls VALID.
Before, the page's viewer said "Bundle verification passed" for a bundle the CLI calls INCOMPLETE.

The bundle What happens
INCOMPLETE or INVALID No page. The command exits as verify --bundle would (3 or 1) and names the claims that did not pass.
A report/v1 root whose rows cite records the bundle does not carry No page, exit 3, naming how many rows it would show ("would show 1 of 2 rows").
From a jsonl profile No page, exit 2. Its bundle carries its evidence book's records about what was sealed, not the records themselves, so a page cannot show them. Publish on a SQLite profile for a page.
  • bundle still writes the bundle (--out, or stdout) when its page is refused.
  • disclose writes nothing and puts no disclosure on record.
  • Unchanged: every page path measured on rc9 was VALID: deal receipts, own and shared, and the README's self-checking report.

A spend limit binds the most a payment may take

The gap: a pay check carried only the expected charge, so an authorization buffer evaded every limit. For example, a $4.54
order passed a $5 limit while the card approval let the merchant take up to $9.54.

The new field: a pay check takes authorized_max_minor, the most the counterparty may take under the payment's authorization
(a card hold, or a pre-authorization with a buffer). It must be at least amount_minor, which stays the expected charge. The intent
limit evaluates it, and a difference names it.

Required for card, wallet and PayPal pays (fail safe): a pay by card, wallet, PayPal, or any rail not known to be holdless is
refused (exit 2) unless it states authorized_max_minor. Give the same value as amount_minor when no larger maximum is shown. A
pay on a holdless rail (bank transfer, Zelle, wire, cash and the like) defaults it to the amount.

Sealed (additive): the check carries authorized_max_minor, and spend_authorized_minor beside spend_minor. A typed
proposed-action/v0 carries it too, so an approval never extends to a larger maximum.

  • The capture: an action that takes more than the authorized maximum differs from its check.
  • Older records: records already sealed re-derive unchanged.

deal check runs the user's pinned rules checker, and seals what it said

A deal profile may now pin an external rules checker. Every deal check runs it and folds its answer into the check's one verdict
and one prompt. capsulectl knows no ruleset of its own: a ruleset id appears in a check only because the pinned checker reported
it.

Pinning (the user's policy, never the agent's): profile update --rules-checker FILE, where FILE is
{"command": ["/absolute/path", "arg", …], "timeout": "10s", "definition_digest": "<64 hex>"}.

  • Where the executable lives: it must sit under a trusted plugin root, and is pinned by its SHA-256.
  • Re-checked every time: every check re-reads the executable once, re-hashes it, and runs a private copy of exactly those
    bytes. A file swapped after the hash is not what runs.
  • Optional: definition_digest pins the ruleset the checker must report.
  • Timeout: 10s by default, at most 60s.

The contract (neutral, versioned v0; checkers vendor these files byte for byte):

  • Input: stdin is one external-check-input/v0 object (skills/deal/profile/external-check-input-v0.schema.json).
    • record: the sealed capsule of the step being checked, with its disclosed record.
    • history: the profile's sealed acts with an amount, from every deal on the profile's own store, sealed in the last 31 days,
      newest first, at most 1,000, in the same shape. An act taken without a check carries its fields under body.unchecked.
    • history_scope: {days, max_records, complete}.
  • Output: exit 0 and one external-check-result/v0 object (skills/deal/profile/external-check-result-v0.schema.json).
    • ruleset_id, definition_digest;
    • verdict: allow, deny, escalate or not_evaluable;
    • an optional tier, as in Result v0: recomputed or judged. An absent tier reads as judged, never recomputed;
    • an optional grade, by reference to Result v0's Grade: self-attested, witnessed or countersigned;
    • findings[]: {id, check, verdict, reason, limit, value}, passes included. Each limit and value is a number, or a string of
      at most 64 characters with no line break.
    • Anything outside the schema is refused.
  • Refusal: any other exit refuses the input, with the cause on stderr.
  • Rolling windows: a rolling window is evaluated over the history supplied. A checker given too little reports the rule
    not_evaluable.

One verdict:

The checker deal check
allow adds nothing
escalate or not_evaluable pauses, naming each failing rule with its limit and value ("Your rules ask for approval: …", "Your rules were not fully checked: …")
deny the new verdict deny: the card names the rule, its limit and the value, and offers only "Hold"; an approval to proceed is refused
configured but changed since pinned, refused the input, timed out, printed anything else, or reported another ruleset digest pauses: "Your rules were not checked: "
none configured does not pause; the text says "Your rules were not checked: no rules checker configured."

Wire changes (additive)....

Read more

capsulectl v0.1.0-rc9

capsulectl v0.1.0-rc9 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 21:12
8e9030e

capsulectl v0.1.0-rc9

Security

verify --bundle now fails a bundle whose disclosed payload was changed.

The flaw. In v0.1.0-rc3 through v0.1.0-rc8, capsulectl verify --bundle checked each disclosed payload against what its
record sealed, but did not let that result change the verdict. A bundle in which a disclosed agent_input or agent_output had
been edited after sealing was reported as:

  • "verdict": "VALID", with exit status 0;
  • "status": "disclosure_mismatch" for that member, listed under disclosures in the same output.

The same releases also reported VALID for a bundle whose disclosures named a record the bundle does not hold.

What was not affected:

  • The checks on the records themselves: record identity, the record signatures, the checkpoint signature, log membership and
    interval coverage. An edited record, or an edited checkpoint, was reported INVALID as before.
  • verify --capsule.
  • The self-checking HTML page. Its own verifier refused such a bundle ("Bundle verification failed"), and showed none of its
    rows. That holds:
    • for bundle --html and disclose --html from rc5 (in rc3 and rc4 those commands' --html was not implemented, and they
      wrote no page);
    • for deal report --html from rc3.

What changes in this release. verify --bundle reports INVALID, exit status 1, when any disclosed member is anything other
than a match or withheld:

  • a mismatch;
  • a member that cannot be disclosed;
  • a member whose record seals no digest for it;
  • a disclosure for a record the bundle does not hold.

What to do. If you relied on verify --bundle from rc3 to rc8 to accept a bundle that discloses payloads, check that bundle
again with this release. Reading the disclosures list in the earlier output shows an edited payload (any status other than
disclosure_match or withheld), but may not show a disclosure for a record the bundle does not hold; only re-checking with this
release catches that case. Bundles that disclose nothing are not affected.

Behaviour changes for existing deal profiles

The checkpoint cadence ticks every 5 minutes

A deal profile's witness cadence, its checkpoint cadence, now defaults to a tick every 5m, give or take 1m. It was every 1h,
give or take 10m.

  • No change to the construction: one cadence-log entry per tick whether or not anything happened, and nothing publishes on
    deal activity.
  • Witness load: about 288 submissions per profile per day, up from 24.
Your profile What happens
Sets no cadence Moves to the new default at upgrade.
Sets both cadence.interval and cadence.jitter Keeps them.
Sets only cadence.jitter May now be refused: the jitter must be under half the 5m default, so under 2m30s. The error names both values. Set cadence.interval (for example 1h), or lower the jitter.

The poll. Schedule capsulectl --profile <name> cll checkpoint cadence --wait-up-to 5m every 5 minutes.

  • deal tick is the same command and stays callable.
  • A run with no tick due publishes nothing. It contacts the witness only to retry a cadence delivery still pending, once that
    delivery's backoff has passed.
  • Keep --wait-up-to: it publishes each tick at its jittered time, not on the scheduler's grid.

Receipts say when a pending checkpoint will reach the witness, from the profile's own cadence. For example: "within about 6m
(every 5m, give or take 1m)".

The action class on deal records, and cancels are never spend

Each check, action and disclosure sealed from this release carries action_class and taxonomy_version: "2" (new optional
x-deal-v0 members, also sealed inside the typed proposed-action/v0 and action-record/v0), so a limit keyed on an action's
class (money.purchase, booking.create, …) can select deal records. The classes are those of the published action taxonomy v2.

  • Committing to a purchase has the same class as paying for it.
  • A pair the table does not name is external_commitment.other.
  • A cancel is never spend. Every cancel seals spend_minor: 0. A cancel with an amount seals direction: "in". A cancel
    that returns a payment it names is money.refund. An unmatched cancel's amount moves to cancelled_amount_minor and it carries
    no amount_minor. A cancellation fee is recorded as fee_minor, not as spend.
  • Shared copies: a cancel's check that carries an amount is now withheld from copies shared with a counterparty.
  • Records already sealed are unchanged and verify as before.

Materiality: sealed at open, a re-pin mid-deal is flagged

These build on the pinned materiality predicate described under "Carried from rc8" below.

  • Sealed at open. deal open seals the predicate the profile pins into the deal's baseline (materiality, additive). It
    comes from the profile, never from the input.
  • A re-pin mid-deal is flagged. A later check under a different predicate carries a materiality_changed difference: it is
    shown on the card and pauses the check.
  • A deleted predicate file refuses with its own message: "materiality predicate file missing; re-pin it with profile update".
  • What is sealed. Sealed records carry the predicate's digest and a commitment to its name and version (label_commitment):
    • the user's own copy opens the commitment (materiality_openings);
    • a copy shared with a counterparty discloses the digest only;
    • shared copies say a predicate change in fixed words, without the predicates' names.
  • Fixed: shared copies withheld every check step once the verdict began recording materiality in rc8. Verdicts that rc8
    sealed with the name and version in the clear stay withheld from shared copies.

Carried from rc8 (not described in its release notes)

Which agent picks pause a deal check is a pinned materiality predicate. Which attributes the agent picked on its own pause a
check is no longer compiled into capsulectl. It is a materiality predicate (materiality-predicate/v0).

  • A profile with no predicate pauses on every attribute the agent picked. To keep the earlier behaviour, pin the example
    predicate, which reproduces it:
    capsulectl --profile NAME profile update --materiality skills/deal/profile/materiality-predicate/neutral.json.
    capsulectl doctor --profile NAME says which applies.
  • Pinned by digest in the profile (materiality.digest). A check refuses, sealing nothing, when the pinned file has changed;
    re-pin it with profile update --materiality FILE.
  • The user's policy. Setting the predicate is the user's to do: a check takes no predicate of its own.
  • Recorded. Every check says which predicate decided (name, version and digest, or digest none) in its output, and seals it
    in its verdict.

New

Typed action records (opt in: deal open --records typed)

A deal can seal its authority steps as typed action records, beside its x-deal-v0 evidence records in one chain:
task-authority/v0, proposed-action/v0, action-evaluation/v0, action-approval/v0, action-record/v0 and
action-outcome/v0. action-report/v0 is defined, and will be sealed by a later release. Deals opened without the flag are
unchanged.

The check contract. The evaluation carries proposed_action_digest, task_authority_ref, ruleset_digest,
materiality_digest, valid_until and authority_basis.

  • A step after valid_until is refused.
  • An approval never extends to a changed proposed action.
  • An ASK is answered only by the user's own approval, on the card the check showed.

Platform approval observations. deal note --kind platform_approval records that another platform asked for approval, and
what it showed and returned. It authorizes nothing.

The receipt's Authority block. For each action in a typed deal, the receipt lists every authority layer it relied on, in the
order of its sealed authority basis, each with its own time:

  1. your request;
  2. your rules (DO or ASK);
  3. your approval on the card;
  4. any platform's own approval, marked as observed;
  5. the action.

The block is in the JSON report, the receipt page and the emailed receipt. It is in the user's own copy only.

The Capsule follows the authority basis. A typed action's Capsule reads its disposition from its sealed authority_basis:

Authority basis Capsule
Within the user's rules (a DO) decide with approver: policy
The user's own approval decide with approver: human
No covered basis fyi

A platform's approval never makes it human.

A self-checking report of records you sealed

The README has a worked example, run in CI exactly as written, that turns sealed records into a bundle plus a page that checks
itself offline. It uses:

  • a SQLite profile with a log;
  • a report/v1 root citing the records;
  • one checkpoint;
  • disclose --out bundle.json --html report.html;
  • verify --bundle.

The README states what VALID rests on: keys the bundle carries, which a reader compares with the producer's key obtained
another way. It also states what the page does not check: the checkpoint's signature and the records' signatures.

Fixes

  • verify --bundle accepts the log id from the completeness certificate. Bundles built on a JSONL profile failed with
    checkpoint_field_missing:log_id, though capsulectl wrote them. The signed log id must now equal every log id the bundle
    states, in the certificate, the checkpoint or both.
  • The deal receipt page renders deal bundles. The vendored evidence-graph viewer threw "root aggregate payload is not
    disclosed" on a bundle with no evaluation summary, and showed a blank page. It now renders the bundle and says it has no
    aggregate view.
  • Shared copies of deals keep their check steps. See "Materiality" above. (One new withholding: a cancel's check with an amount; see "The action class" above.)

Dependencies

  • `githu...
Read more

capsulectl v0.1.0-rc8

capsulectl v0.1.0-rc8 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 03:40
2100803

Prebuilt capsulectl binaries (linux/amd64, linux/arm64, darwin/arm64). Verify with: sha256sum -c SHA256SUMS. Check the build attestation with no GitHub account: gh attestation verify FILE --bundle capsulectl-v0.1.0-rc8.sigstore.json --repo action-state-group/capsule-cli --signer-workflow action-state-group/capsule-cli/.github/workflows/release.yml

capsulectl v0.1.0-rc7

capsulectl v0.1.0-rc7 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Oct 04:44
dce9add

Prebuilt capsulectl binaries (linux/amd64, linux/arm64, darwin/arm64). Verify with: sha256sum -c SHA256SUMS. Check the build attestation with no GitHub account: gh attestation verify FILE --bundle capsulectl-v0.1.0-rc7.sigstore.json --repo action-state-group/capsule-cli --signer-workflow action-state-group/capsule-cli/.github/workflows/release.yml

capsulectl v0.1.0-rc6

capsulectl v0.1.0-rc6 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 05 Oct 04:20
14bf3bc

Prebuilt capsulectl binaries (linux/amd64, linux/arm64, darwin/arm64). Verify with: sha256sum -c SHA256SUMS. Check the build attestation with no GitHub account: gh attestation verify FILE --bundle capsulectl-v0.1.0-rc6.sigstore.json --repo action-state-group/capsule-cli --signer-workflow action-state-group/capsule-cli/.github/workflows/release.yml