diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74be60d..649bef3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,16 @@ prereleases follow the Java development line (`0.7.0-alpha.N` alongside `0.7.0-S
## [Unreleased]
+## [0.7.0] - 2026-10-30
+
+A release of hardening, matching easyssf for Java 0.7.0. The poll endpoint is held to the URI
+policy of the transmitter's metadata endpoints before the access token goes to it, responses from
+the transmitter are bounded, 4 MiB by default, and every stream configuration the transmitter
+returns has its issuer checked, not only a created one. Stopping and starting the stream registrar
+again no longer leaves a stream behind or deletes the wrong one. Java's fixes for a host name like
+`127.attacker.example` passing for loopback and for the pending-acks gauge turning `NaN` needed no
+counterpart: neither bug occurs in .NET.
+
### Security
- The poll endpoint is held to the URI policy of the transmitter's metadata endpoints before the
@@ -114,6 +124,7 @@ an in-process test transmitter. Six packages under the reserved prefix `EasySsf`
conformance suite with push and poll delivery. Both are built with the solution and not
published.
-[Unreleased]: https://github.com/easyssf/easyssf-dotnet/compare/v0.6.0...HEAD
+[Unreleased]: https://github.com/easyssf/easyssf-dotnet/compare/v0.7.0...HEAD
+[0.7.0]: https://github.com/easyssf/easyssf-dotnet/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/easyssf/easyssf-dotnet/compare/v0.6.0-alpha.1...v0.6.0
[0.6.0-alpha.1]: https://github.com/easyssf/easyssf-dotnet/releases/tag/v0.6.0-alpha.1
diff --git a/Directory.Build.props b/Directory.Build.props
index be1b42f..544e430 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
latest
enable
enable
- 0.7.0-alpha.1
+ 0.7.0
easyssf contributors
Apache-2.0
diff --git a/README.md b/README.md
index b92cbe5..7cb27ce 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ behaviour; the two share their test suites, their database tables and their Keyc
| | |
|---|---|
-| **Status** | Experimental, feature complete relative to the Java library; on [NuGet.org](https://www.nuget.org/profiles/easyssf) since `0.6.0`, and below 1.0 a minor version may break the API |
+| **Status** | Experimental, feature complete relative to the Java library; on [NuGet.org](https://www.nuget.org/profiles/easyssf) since `0.7.0`, and below 1.0 a minor version may break the API |
| **.NET** | 10, the current LTS; older runtimes leave support in November 2026 and are not targeted |
| **Scope** | Receiver only; a transmitter is not planned, `EasySsf.Core` is written so that one could share it |
| **Package prefix** | `EasySsf` |
@@ -102,8 +102,8 @@ behaviour; the two share their test suites, their database tables and their Keyc
Reference `EasySsf.Receiver.Hosting` and, for push delivery, `EasySsf.Receiver.AspNetCore`:
```xml
-
-
+
+
```
Configure the transmitter, register the receiver and your handlers, and map the push endpoint:
@@ -227,7 +227,7 @@ unknown to the others. `EasySsf.Receiver.Data` keeps that state in the database
application instead, in three tables that are the same as those of the Java library:
```xml
-
+
```
| Table | Store | Content |
@@ -373,7 +373,7 @@ The meter `EasySsf.Receiver` records, under the names of the Java library:
Without a framework, `EasySsf.Receiver` is the package to reference (it brings `EasySsf.Core`):
```xml
-
+
```
```csharp
@@ -580,7 +580,7 @@ Set and metadata to an `HttpClient` in-process, without a socket. The tests of e
it.
```xml
-
+
```
```csharp
diff --git a/tests/EasySsf.Receiver.Tests/Stream/SsfStreamManagementTests.cs b/tests/EasySsf.Receiver.Tests/Stream/SsfStreamManagementTests.cs
index 05d9061..9ab6775 100644
--- a/tests/EasySsf.Receiver.Tests/Stream/SsfStreamManagementTests.cs
+++ b/tests/EasySsf.Receiver.Tests/Stream/SsfStreamManagementTests.cs
@@ -16,6 +16,9 @@ public sealed class SsfStreamManagementTests : IClassFixture, I
/// how long StopAsync() waits for a registration that does not end, shortened
private static readonly TimeSpan StopTimeout = TimeSpan.FromMilliseconds(200);
+ /// how early a timer may fire relative to the stopwatch, generously
+ private static readonly TimeSpan TimerResolution = TimeSpan.FromMilliseconds(50);
+
private readonly List _clients = [];
private readonly TestTransmitter _transmitter;
@@ -389,7 +392,11 @@ public async Task RegistrationThatOutlivesItsStopLeavesTheStreamToTheRegistratio
// one, which finds the stream the first created and adopts it
long stopping = TimeProvider.System.GetTimestamp();
await registrar.StopAsync();
- Assert.True(TimeProvider.System.GetElapsedTime(stopping) >= StopTimeout);
+ // StopAsync() waited for the registration, give or take the resolution of the timer, which
+ // may fire a little before the stopwatch measures the full timeout (seen on a CI runner)
+ Assert.True(TimeProvider.System.GetElapsedTime(stopping) >= StopTimeout - TimerResolution);
+ Assert.False(registrar.IsRunning);
+ Assert.Empty(registered);
registrar.Start();
await Eventually.UntilAsync(() => _receiverStream.StreamId is not null);
Assert.Equal(streamId, _receiverStream.StreamId);