Skip to content

chore: release - #1277

Merged
stormer78 merged 1 commit into
mainfrom
release-plz-2026-09-07T07-30-55Z
Sep 7, 2026
Merged

chore: release#1277
stormer78 merged 1 commit into
mainfrom
release-plz-2026-09-07T07-30-55Z

Conversation

@stormer78

@stormer78 stormer78 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

🤖 New release

  • vta-sdk: 0.33.0 -> 0.34.0 (✓ API compatible changes)
  • vti-common: 0.17.0 -> 0.18.0 (✓ API compatible changes)
  • vti-rooms: 0.1.1 -> 0.1.2 (✓ API compatible changes)
  • vtc-client: 0.5.3 -> 0.5.4 (✓ API compatible changes)
  • vta-cli-common: 0.13.0 -> 0.14.0 (✓ API compatible changes)
  • cnm-cli: 0.13.6 -> 0.14.0
  • pnm-cli: 0.14.6 -> 0.15.0
  • vta-persona: 0.1.0 -> 0.2.0 (✓ API compatible changes)
  • vta-service: 0.23.5 -> 0.24.0 (✓ API compatible changes)
  • vti-rooms-dtg: 0.1.1 -> 0.1.2
  • vta-audit: 0.3.3 -> 0.3.4
  • vti-secrets: 0.3.2 -> 0.3.3
  • vta-config: 0.4.3 -> 0.4.4
  • vta-keyspaces: 0.2.6 -> 0.2.7
  • vta-keys: 0.4.3 -> 0.4.4
  • vta-support: 0.3.2 -> 0.3.3
  • vta-backup: 0.3.3 -> 0.3.4
  • vta-policy: 0.3.3 -> 0.3.4
  • vta-vault: 0.5.3 -> 0.5.4
  • vta-sweepers: 0.3.3 -> 0.3.4
  • vta-tee: 0.2.3 -> 0.2.4
  • vta-webvh: 0.2.4 -> 0.2.5
Changelog

vta-sdk

0.34.0 — 2026-09-07

Added

  • rooms: A member's CLI surface, driven through the oracle (#1285)

Rooms had no CLI. Using one meant writing Rust against vtc-client or hand-
building signed Trust Task documents, which is not a surface an operator has.
pnm rooms {create,list,get,put,curate,renew} is that surface for a member.

vti-common

0.18.0 — 2026-09-07

Added

  • acl: Enforce an entry's capabilities, and give an operator a way to set them (#1279)

Fixed

  • acl: Let the role an agent runs as reach the room oracle (#1275)

The room presentation oracle exists for one consumer: an agent holding
strictly less than its human, asking its principal's VTA to mint a scoped
presentation and to open what it cannot decrypt. application is the role
such an agent runs as - vta-agent-memory grants exactly it - and it held
neither RoomPresent nor RoomOpen, so the one consumer the oracle was
built for could not call it.

The gates are role-derived (role_has_capability reads the role and nothing
else), so there was no way to grant the capability to a particular entry
either. The workaround an operator reaches for is worse than the grant:
running the agent as initiator, which carries KeyMint and DeviceAdmin
besides.

Neither capability widens what this role can do. RoomPresent mints a leaf
attenuated from the principal's own room authority - one action, one room,
four hours, bound to the caller - and the role already holds Sign, which is
the principal's key over arbitrary bytes and therefore strictly more.
RoomOpen decrypts a room record under a group key the VTA already holds,
and the same role can already read the credential vault. Reader and
Monitor get neither, and a test pins that: minting a credential on a
principal's behalf is not a read.

Also corrects two doc comments that described enforcement that does not
exist. AclEntry::capabilities said the auth layer falls back to the
role-derived set when it is empty, which reads as "and uses this set when it
is not" - but no gate consults the field, the authenticated claims carry no
capability set, and nothing over the wire can set it. It is read in exactly
one place, to describe a registered device's authority in a binding listing.
A reader who believed otherwise would think an entry was least-privileged
when it holds everything its role does, so the field and
role_has_capability now say what is true. Making it real means carrying the
set in the claims and giving the ACL surface a way to set it - a separate
change with its own design question about whether an entry's set may widen
beyond its role or only narrow within it.

vti-rooms

0.1.2 — 2026-09-07

Fixed

  • rooms: Authorize registering a room, which nothing did (#1274)

rooms/create/0.1 was the one verb neither host authorized. The document's
proof was never verified on that path and the signer was never compared to
ownerDid, so anyone who could reach the endpoint could register a room row
naming any party as its owner - filling a host's store with rooms attributed
to people who never agreed to own them, and taking identifiers from under
their real owners. Every other verb verifies the proof and the authority
chain; this one fell through because it is the one operation no chain can
authorize, and the check it needed instead was never written.

Create cannot be authorized by a chain: at the moment it runs the room has
issued nothing, so no credential in the world speaks for it. What a host does
have is the proof on the request, and that is what makes ownerDid a fact
rather than a field anyone can fill with anyone. So the presenter must be the
party they name as owner, and the room is then written from the authorization
rather than from the payload.

The decision lives in vti_rooms::authz::authorize_create, beside the chain
authorization it complements, because a room host and a VTC disagreeing about
who may register a room is exactly the class of drift that crate exists to
prevent. It returns an AuthorizedCreate with no public constructor, so a
handler that skips the check does not compile - the same typestate the rest
of the family uses.

What this deliberately does not do is prove control of the identifier. A
party can still register a roomId they do not control while naming
themselves owner, denying that id to its real owner on that host. The row
confers nothing - every later verb needs credentials the real room issued -
so it is a nuisance rather than a takeover, and bounding it is quota and
access control, which is the availability row of the trust model. Proving
control would mean the room signing its own registration, which would exclude
every owner whose room key cannot sign a request document.

Behaviour change for any caller that relayed a registration on an owner's
behalf: there were none in the workspace, and the two hosts, their tests and
the data_room example all already signed as the owner.

vtc-client

0.5.4 — 2026-09-07

Added

  • rooms: A member's CLI surface, driven through the oracle (#1285)

Rooms had no CLI. Using one meant writing Rust against vtc-client or hand-
building signed Trust Task documents, which is not a surface an operator has.
pnm rooms {create,list,get,put,curate,renew} is that surface for a member.

vta-cli-common

0.14.0 — 2026-09-07

Added

  • rooms: A member's CLI surface, driven through the oracle (#1285)

Rooms had no CLI. Using one meant writing Rust against vtc-client or hand-
building signed Trust Task documents, which is not a surface an operator has.
pnm rooms {create,list,get,put,curate,renew} is that surface for a member.

cnm-cli

0.14.0 — 2026-09-07

Added

  • acl: Create an entry already narrowed, rather than narrowing it after (#1280)

#1279 left acl/grant refusing a capability narrowing and pointing at
acl update, because taking one meant a fifteenth positional parameter on
create_acl. Refusing was honest but it leaves a real window: between the
grant and the narrowing the entry holds everything its role implies, and a
subject that authenticates inside that window is authorized by what it found
there.

create_acl now takes a CreateAclParams struct - the shape update_acl
already had - so the narrowing is one more named field rather than a
fourteenth argument nobody can read at the call site. Test call sites state
the two or three members they care about and default the rest instead of
spelling every one to reach the last.

The rule is the update path's, applied where the entry is born: a name the
role does not carry is refused rather than dropped, an unknown name is
refused, and neither leaves a row behind. pnm acl create --capabilities memory-read,room-present is now the form to prefer, and the agent runbook
says so.

Also echoes the stored narrowing from acl create and acl show, so the
restriction can be read back from wherever it was set.

  • acl: Enforce an entry's capabilities, and give an operator a way to set them (#1279)

pnm-cli

0.15.0 — 2026-09-07

Added

  • rooms: A member's CLI surface, driven through the oracle (#1285)

Rooms had no CLI. Using one meant writing Rust against vtc-client or hand-
building signed Trust Task documents, which is not a surface an operator has.
pnm rooms {create,list,get,put,curate,renew} is that surface for a member.

vta-persona

0.2.0 — 2026-09-07

Fixed

  • persona: The audit trail says what changed, and correlation/analyze conforms (#1283)
  • fix(persona): the audit trail says what changed

    Every persona write recorded action, actor, resource and outcome and
    nothing else. audit_persona called audit::record(...), which has no
    detail parameter, so a console audit pane showed twenty rows of
    persona.attribute.put against opaque ULIDs with no way to tell a
    create from an update, a cascade delete from a no-op against a typo'd
    id, or a binding that materialised forty claims from one that cleared
    them.

    The console side was never the problem: AuditEnvelope already renders
    detail in full as detail.reason. There was simply nothing to render.

    So the write handlers now go through audit::record_with_detail and
    supply one: attribute put/delete, profile put/delete, binding/set, and
    the three context-local writes each say what changed — created or
    updated, the claim type, the value type, the provenance kind, entry and
    claim counts, whether the record existed, how many profiles or personas
    were affected, and the resulting version. Reads still pass None; a
    read changes nothing, and a sentence restating the request would be
    noise in an append-only store.

    The attribute VALUE stays out, and audit_persona now explains why at
    length, because the reason is not the one people assume. It is NOT an
    access-control reason. Persona rows are recorded with context_id: None, and operations::audit::authorize already refuses every entry
    not confined to a named context to anyone but an unrestricted admin —
    exactly the caller Reach::Holder admits to attribute/list, which
    returns the plaintext outright. Nobody gains a read by us writing one.

    The reason is lifetime. The audit keyspace is append-only and pruned on
    its own retention schedule; the pool is deleted when the holder deletes
    an attribute. Copy a value across and attribute/delete quietly stops
    being a delete — the value outlives the record it came from, in a store
    the holder's delete does not reach. Stating that distinction matters,
    because "don't log values" as a bare prohibition is the rule somebody
    relaxes the first time an operator asks for a better trail, and the
    access-control argument does not survive that conversation.

    The new test asserts both halves together. A test that only checks the
    value is absent passes against a handler that records no detail at all,
    which is precisely the state being fixed.

  • persona: "edit once, everywhere" reached nothing (#1281)

A holder edited their name and the verifier was still shown the old
one. rematerialise — the function whose docstring explains that a
context holds a materialised copy and can only be updated by a write
from above the boundary — was called from no handler in the
codebase
. The only reference outside its own file was a comment in a
test.

A context may never read the pool, so its copy changes only if
something pushes. Nothing did. attribute/put, attribute/delete --cascade and profile/put all changed what a profile projects and
left every bound context presenting the state from before the edit,
which disclosure/preview and disclosure/present then handed to a
verifier.

The push now belongs to the write, not to the call site. put,
delete and put_profile push before returning, from inside the lock
they already hold. Leaving it to handlers is the same decision taken
once per call site and forgetting it is silent — the pool shows the new
value, the console shows the pool, and only the verifier sees the old
one.

vta-service

0.24.0 — 2026-09-07

Added

  • acl: Create an entry already narrowed, rather than narrowing it after (#1280)

#1279 left acl/grant refusing a capability narrowing and pointing at
acl update, because taking one meant a fifteenth positional parameter on
create_acl. Refusing was honest but it leaves a real window: between the
grant and the narrowing the entry holds everything its role implies, and a
subject that authenticates inside that window is authorized by what it found
there.

create_acl now takes a CreateAclParams struct - the shape update_acl
already had - so the narrowing is one more named field rather than a
fourteenth argument nobody can read at the call site. Test call sites state
the two or three members they care about and default the rest instead of
spelling every one to reach the last.

The rule is the update path's, applied where the entry is born: a name the
role does not carry is refused rather than dropped, an unknown name is
refused, and neither leaves a row behind. pnm acl create --capabilities memory-read,room-present is now the form to prefer, and the agent runbook
says so.

Also echoes the stored narrowing from acl create and acl show, so the
restriction can be read back from wherever it was set.

  • acl: Enforce an entry's capabilities, and give an operator a way to set them (#1279)

Fixed

  • persona: The audit trail says what changed, and correlation/analyze conforms (#1283)
  • fix(persona): the audit trail says what changed

    Every persona write recorded action, actor, resource and outcome and
    nothing else. audit_persona called audit::record(...), which has no
    detail parameter, so a console audit pane showed twenty rows of
    persona.attribute.put against opaque ULIDs with no way to tell a
    create from an update, a cascade delete from a no-op against a typo'd
    id, or a binding that materialised forty claims from one that cleared
    them.

    The console side was never the problem: AuditEnvelope already renders
    detail in full as detail.reason. There was simply nothing to render.

    So the write handlers now go through audit::record_with_detail and
    supply one: attribute put/delete, profile put/delete, binding/set, and
    the three context-local writes each say what changed — created or
    updated, the claim type, the value type, the provenance kind, entry and
    claim counts, whether the record existed, how many profiles or personas
    were affected, and the resulting version. Reads still pass None; a
    read changes nothing, and a sentence restating the request would be
    noise in an append-only store.

    The attribute VALUE stays out, and audit_persona now explains why at
    length, because the reason is not the one people assume. It is NOT an
    access-control reason. Persona rows are recorded with context_id: None, and operations::audit::authorize already refuses every entry
    not confined to a named context to anyone but an unrestricted admin —
    exactly the caller Reach::Holder admits to attribute/list, which
    returns the plaintext outright. Nobody gains a read by us writing one.

    The reason is lifetime. The audit keyspace is append-only and pruned on
    its own retention schedule; the pool is deleted when the holder deletes
    an attribute. Copy a value across and attribute/delete quietly stops
    being a delete — the value outlives the record it came from, in a store
    the holder's delete does not reach. Stating that distinction matters,
    because "don't log values" as a bare prohibition is the rule somebody
    relaxes the first time an operator asks for a better trail, and the
    access-control argument does not survive that conversation.

    The new test asserts both halves together. A test that only checks the
    value is absent passes against a handler that records no detail at all,
    which is precisely the state being fixed.

  • persona: "edit once, everywhere" reached nothing (#1281)

A holder edited their name and the verifier was still shown the old
one. rematerialise — the function whose docstring explains that a
context holds a materialised copy and can only be updated by a write
from above the boundary — was called from no handler in the
codebase
. The only reference outside its own file was a comment in a
test.

A context may never read the pool, so its copy changes only if
something pushes. Nothing did. attribute/put, attribute/delete --cascade and profile/put all changed what a profile projects and
left every bound context presenting the state from before the edit,
which disclosure/preview and disclosure/present then handed to a
verifier.

The push now belongs to the write, not to the call site. put,
delete and put_profile push before returning, from inside the lock
they already hold. Leaving it to handlers is the same decision taken
once per call site and forgetting it is silent — the pool shows the new
value, the console shows the pool, and only the verifier sees the old
one.


This PR was generated with release-plz.

@stormer78
stormer78 requested a review from a team as a code owner September 7, 2026 07:30
@stormer78 stormer78 changed the title chore(vti-rooms): release v0.1.2 chore: release Sep 7, 2026
@stormer78
stormer78 force-pushed the release-plz-2026-09-07T07-30-55Z branch from bfe1992 to afd0d85 Compare September 7, 2026 07:35
@affinidi-appsecurity-bot

Copy link
Copy Markdown

🛡️ AI Agentic Security Code Review — all clear. We checked this change and found nothing to report. Keep shipping secure code!

Note: for major, breaking, or feature-introducing changes, you can always request an in-depth review from the security team.

@stormer78
stormer78 force-pushed the release-plz-2026-09-07T07-30-55Z branch 8 times, most recently from a37fba0 to 208a671 Compare September 7, 2026 11:47
@stormer78
stormer78 force-pushed the release-plz-2026-09-07T07-30-55Z branch from 208a671 to 45e1b78 Compare September 7, 2026 12:19
@stormer78
stormer78 merged commit 72bf579 into main Sep 7, 2026
17 of 19 checks passed
@stormer78
stormer78 deleted the release-plz-2026-09-07T07-30-55Z branch September 7, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants