Current section
Files
Jump to
Current section
Files
CHANGELOG.md
# Changelog
All notable changes to `layr8`. Format loosely follows [Keep a Changelog](https://keepachangelog.com/); versioning follows [SemVer](https://semver.org/).
This file starts here. Earlier releases (through 0.2.8) are recorded only in git history.
## [Unreleased]
## [0.2.9] - 2026-08-10
### Added
- **Verifiable Grants are attached to outbound messages** — automatically, on
every send path (`send/3`, `request/3`, and a handler's reply). The cloud-node
requires a grant for anything its policy does not allow outright, and nothing
in this SDK attached one: an agent that connected directly sent nothing and
was denied with "no grant covers this call", a message that reads as "your
grant is misconfigured" when the truth is "no credential was ever put on the
wire".
`Layr8.Wallet` reads the holder's credentials from the node, caches them for
`:grant_cache_ms` (default 60s) and selects the covering set with a mirror of
helix's `structure_v2.rego`. Caller-supplied attachments are never displaced,
and a wallet failure never blocks the send.
New config: `:attach_grants` (default `true`, env `LAYR8_ATTACH_GRANTS`),
`:grant_cache_ms`, `:grant_read_timeout_ms`, `:on_grant_miss`. New API:
`Layr8.Client.refresh_grants/2`.
Contract: `contracts/sender-cn-vg-attachment.md`.
- **`:on_grant_miss`** — told when the node denied a message that went out with
nothing attached, when the covering set had to be capped at 16, or when the
grants could not be read at all. It deliberately stays quiet on "nothing
covered this message" alone: most traffic (discovery, trust-ping, problem
reports) needs no grant.
- **`Layr8.Mcp`** — MCP over DIDComm. `Layr8.Client.mcp/2` registers the
protocol subscription and returns a binding; `Layr8.Mcp.peer/2` yields a
caller with `initialize/3`, `list_tools/2` and `call_tool/4`. It handles the
`tools/call` → `#{base}/tools-call` type mapping, the JSON-RPC envelope and
unwrapping `result`. Must be called before `connect/1`, like `handle/3`.
Contract: `contracts/mcp-over-didcomm.md`.
- **`Layr8.Client.request_result/3`** — `request/3` without the raises, for
callers that route on failure rather than rescuing it. `Layr8.Mcp` uses it,
which is why its whole surface is tagged tuples.
- **`:rest_timeout_ms`** (default 30s, env `LAYR8_REST_TIMEOUT_MS`) — a deadline
on every credential and presentation call, not just the grant read. `Req` has
none of its own, so a node that accepted the connection and went quiet left
those calls hanging. `0` disables it.
### Changed
- **Every send now performs a credential read against the node before the
message goes out** (once per `:grant_cache_ms` per DID; failures are cached
for a shorter window so a misconfigured API key is not a per-message round
trip). A node that cannot serve `/api/v1/credentials` degrades to sending
unattached — the previous behaviour — and `:on_grant_miss` reports it. Set
`attach_grants: false` to opt out entirely.
- **`Layr8.REST` no longer retries.** `Req` retries safe requests by default
with a 1s/2s/4s backoff, and because `receive_timeout` is per attempt, that
silently multiplied every deadline by four: measured, a 2s grant read against
a node that accepts the connection and goes quiet took **7.6 seconds** to
return. A deadline exists here so a hung node cannot stall the sends queued
behind it, so `retry: false` now goes wherever a deadline goes. Retrying is
the caller's decision, with its own knowledge of whether the call is worth
repeating.
- `Layr8.REST.new/2`, `get/2` and `post/3` gained optional trailing arguments;
the existing arities are unchanged.
### Fixed
- **`Layr8.Attachment`'s documentation pointed at the wrong field.** It said to
put `application/vc+jwt` in `format` and to carry credentials in
`data.base64`. `media_type` is the ONLY field the node's credential extractor
filters on, by exact string equality, and it drops everything else *before
looking at the data* — producing a denial byte-for-byte identical to the one
for attaching nothing. Following those docs attached nothing at all. The same
correction is on `Layr8.Presentations.sign_presentation/3`, which is not the
authorization path either.
[0.2.9]: https://github.com/layr8/elixir_sdk/releases/tag/v0.2.9