Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ Deliberate choices a change can silently undo — each looks like a bug until yo
- **Auth commits in one ordered boundary** — claims, then config and provider stamp, then tokens —
behind a totalized result. `kcap login` never repoints `server_url`; `kcap setup` and the wizard
adopt it.
- **Secret redaction rewrites decoded JSON string values, never the serialized line.** A pattern run
over the whole line matches past the value it found into the surrounding structure, and the server
drops an unparseable line silently. A line the writer refuses is replaced by a placeholder —
never by the raw line, which would re-expose what the redactor just matched.

## Tech stack

Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<PackageVersion Include="Avalonia.Themes.Fluent" Version="12.1.1" />
<PackageVersion Include="Duende.IdentityModel.OidcClient" Version="7.1.0" />
<PackageVersion Include="Duende.IdentityModel" Version="8.1.0" />
<PackageVersion Include="DotNext" Version="6.6.2" />
<PackageVersion Include="DynamicData" Version="9.4.33" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.11" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.11" />
Expand Down
30 changes: 30 additions & 0 deletions docs/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@ Not release notes. Each entry is written as of the change that produced it and i
code moves on; where an entry disagrees with the code, the code wins.


## Secret redaction is structural

`SecretRedactor.RedactLine` walks the line token by token and rewrites one JSON value at a time.
Scanning the serialized line cannot be made safe: `AuthHeaderRegex`'s value class excludes `"` and
`\` but not `{`, `}`, `[`, `]`, `,` or `:`, so a header-named key carrying an object or a number had
the match run past the value and swallow the structure after it. The server drops a line it cannot
parse without saying so, which is what made the damage invisible.

Decoding first keeps a serialized tool result carried as a string in scope, and lets the key
vocabularies run against a real JSON property name — which no text pattern can see, the key and the
value being separate tokens. A secret-bearing key arms every leaf beneath it, so
`{"auth":["b1","b2"]}` redacts both elements and keeps the array. Numbers are exempt whatever the
key: the keyword vocabulary matches anywhere in a name, so `token_count` and `input_tokens` — read
as metrics, and present on nearly every model turn — would otherwise be rewritten. The all-digit
credential is the deliberate price, and a header value arrives as a string. A name that is itself a
credential is replaced outright and numbered, since two siblings sharing one marker would collide
into a duplicate key.

Every token goes straight back out through a `Utf8JsonWriter`, so a mangled document is not
representable. The reader's depth limit is System.Text.Json's own ceiling, which is also the
writer's: anything the reader accepts the writer can emit, leaving the whole-line pipeline only
input no reader would take — where re-checking the result would mean re-parsing what just failed to
parse. A comment is dropped rather than re-emitted, since strict JSON has none; the drop counts as
a change on its own, or a line whose values are clean would ride the unchanged path still carrying
it.

A line whose values all survive is handed back as it arrived rather than as the writer re-encoded
it, so the common case reaches the wire byte for byte. Once anything is redacted that no longer
holds: the whole line is the writer's, escaping and spacing normalised.

## The Agents screen's visibility answer reaches the profile

The flow asked who may read future sessions, recorded it on `FirstRunAgentsDecidedEvent`, served it on
Expand Down
1 change: 1 addition & 0 deletions src/Capacitor.Cli/Capacitor.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<ProjectReference Include="..\Capacitor.Cli.Core\Capacitor.Cli.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DotNext" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Data.Sqlite" />
Expand Down
Loading