Skip to content

Releases

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 15:20
a41fd24

Adds WebGLFileSystem.Persist() to the Unity package, so an outbox queue in a WebGL player survives a page reload, and widens the Unity tests to WebGL in CI, Android and the iOS Simulator. The NuGet packages have no code changes. The LRS still passes both ADL conformance batteries in full on SQLite and PostgreSQL (xAPI 2.0.0: 1435 of 1435, 1.0.3: 1365 of 1365) and the 5,299 tests pass on Linux. Of the 12 Unity tests, 11 pass in the editor and in a Linux IL2CPP player, where the reload test is skipped; in WebGL 8 pass and the 4 that need HttpClientHandler are skipped, before and after the reload.

Added

  • Xapi.Unity: WebGLFileSystem.Persist() saves Application.persistentDataPath to IndexedDB in WebGL players, where Unity writes that directory only when asked, so a FileOutboxStore queue there survives a page reload. Outside WebGL it does nothing. The WebGL tests check it across a reload.
  • tests/Unity: CI also runs the tests in a WebGL player, in headless Chrome, and they pass in Android and iOS IL2CPP players on an arm64 Android emulator and the iOS Simulator. CONTRIBUTING.md has the steps for both. See docs/unity.md.

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 27 Oct 09:55
292e16d

A security update of the xapi-lrs image. The NuGet packages and the Unity package have no code changes and are released again so that every version matches the image. The LRS still passes both ADL conformance batteries in full on SQLite and PostgreSQL (xAPI 2.0.0: 1435 of 1435, 1.0.3: 1365 of 1365), the 5,299 tests pass on Linux, and the 11 Unity tests pass in the editor and in a Linux IL2CPP player.

Security

  • The xapi-lrs image runs on the ASP.NET Core 10.0.14 runtime (was 10.0.13), the .NET security release of 25 October 2026 with fixes for four CVEs, and is built with SDK 10.0.403.

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 16:47
6879c0d

The first release without a preview suffix. It adds Unity WebGL support through a new Unity package; the public API of the NuGet packages is unchanged. The LRS still passes both ADL conformance batteries in full on SQLite and PostgreSQL (xAPI 2.0.0: 1435 of 1435, 1.0.3: 1365 of 1365), the solution has about 5,300 tests, and the 11 Unity tests pass in the editor, in a Linux IL2CPP player and, locally, in WebGL.

Added

  • samples/LrsHost and the xapi-lrs image: LrsHost:AllowedOrigins (LrsHost__AllowedOrigins in Docker) turns on CORS for the listed origins, so xAPI content in a browser on another origin can use the LRS. It is off by default.
  • tests/Unity: Unity 6.3 tests of Xapi.Core, Xapi.Client and Xapi.Client.Outbox, installed with NuGetForUnity, that run in the editor, in IL2CPP players and in WebGL; CI runs them on Linux against the packages packed from the commit, and tests/Unity/WebGL runs a WebGL build in headless Chromium. See docs/unity.md.
  • A Unity package, io.github.nurullahturkoglu.xapi.unity in src/Xapi.Unity, installed from git: UnityWebRequestHandler, an HttpMessageHandler that sends requests with UnityWebRequest, so XapiClient works in WebGL players, where HttpClient's default handler sends nothing.

Fixed

  • FileOutboxStore.OpenAsync never completed in Unity WebGL players when the store already had a journal, because the netstandard2.0 build read it with asynchronous file I/O, which needs the thread pool. That build now reads the journal synchronously; the net10.0 build is unchanged.

v0.1.0-preview.1

v0.1.0-preview.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 17 Oct 10:22
09d36ae

The first preview of all six packages. The LRS passes both ADL conformance batteries in full on SQLite and PostgreSQL (xAPI 2.0.0: 1435 of 1435, 1.0.3: 1365 of 1365), and the solution has about 5,300 tests.

Added

  • Xapi.Core: immutable models for xAPI 1.0.3 and 2.0.0 (IEEE 9274.1.1), strict and lenient JSON reading with all errors reported by JSON path, JSON writing in exact, ids and canonical formats, statement and batch validation, and statement equivalence as defined by the standard. Targets netstandard2.0 and net10.0.
  • The 1,922 statement cases generated by the ADL LRS conformance test suite templates (950 for 1.0.3, 972 for 2.0.0) run as unit tests against the parser.
  • CI runs both ADL conformance batteries against lrsql to keep the conformance pipeline itself tested.
  • Xapi.Client: an HttpClient-based client for any xAPI 1.0.3 or 2.0.0 LRS. Covers statements (save, batch, get, voided, query with more paging, void), state, activity and agent profile documents with ETag preconditions, agents, activities and about. Sends and reads attachments as multipart/mixed, negotiates the version through /about when none is set, and maps 409 and 412 to typed exceptions. AddXapiClient registers it with IHttpClientFactory on net10.0. Targets netstandard2.0 and net10.0 and is Native AOT compatible.
  • Xapi.Client.Outbox: a durable outbox for statements. StatementOutbox assigns ids before storing, so resends are idempotent, and delivers in order and in batches, with exponential backoff and jitter for transient errors, Retry-After, bisection of rejected batches, 409 resolution through StatementEquivalence, a pause on 401 and 403, and inspectable, requeueable dead letters. FileOutboxStore keeps an append-only, checksummed journal that survives a process killed mid-write; InMemoryOutboxStore is for tests. Runs with FlushAsync, with a background loop that needs no host, or as a hosted service through AddXapiClient(...).AddOutbox(...) on net10.0. Targets netstandard2.0 and net10.0. See docs/outbox.md.
  • Xapi.Lrs: an LRS that runs inside an ASP.NET Core app (AddXapiLrs, MapXapiLrs). It negotiates xAPI 1.0.3 and 2.0, authenticates with Basic credentials (PBKDF2-hashed) or a delegated ASP.NET Core scheme, applies the xAPI scopes, and serves About and the Statement Resource write path: PUT and POST with strict validation, conflict detection by statement equivalence, voiding, one transaction per batch, and GET of single statements by statementId or voidedStatementId. Statement queries support every filter of the standard (including related_agents, related_activities and matches through StatementRefs, with voided statements left out), the exact, ids and canonical formats with Accept-Language, stateless more paging keyed on the stored order (MaxStatementsPerPage), Last-Modified and X-Experience-API-Consistent-Through. xAPI 1.0.x requests may use the Alternate Request Syntax. It also serves the State, Activity Profile and Agent Profile Resources: GET of one document or of the ids (with since), PUT, POST with JSON merge, DELETE of one document or of all state (with or without a registration), HEAD, SHA-1 ETags and Last-Modified, If-Match / If-None-Match checked atomically with the write, and the 409 and 400 rules for missing preconditions that differ between 1.0.3 and 2.0. Documents are limited to MaxDocumentBytes (1 MiB by default). The Agents and Activities Resources return a Person and the canonical Activity definition. Targets net10.0.
  • Xapi.Lrs: statement attachments. PUT and POST accept multipart/mixed, match parts to Attachment objects by SHA-2 (one part may serve several statements of a batch) and reject missing, extra or mismatching parts; data is stored once per SHA-2 and limited by MaxAttachmentBytes (50 MiB per request by default, 413 above it). attachments=true returns the statements with their attachment data as multipart/mixed, for single statements and queries.
  • Xapi.Lrs: signed statements are checked as IEEE 9274.1.1, 4.2.6 requires: an RS256, RS384 or RS512 JWS whose payload is equivalent to the statement (durations to 0.01 second), verified with the x5c certificate when there is one. Malformed signatures get 400. See docs/signatures.md.
  • Xapi.Client: StatementSigner signs a statement with an RSA key and an optional certificate chain, and returns the statement with its signature attachment and the JWS to send with SaveStatementAsync.
  • Xapi.Lrs.Sqlite and Xapi.Lrs.PostgreSql: EF Core storage with migrations (UseSqlite, UsePostgreSql, MigrateOnStartup). The storage extension points (IStatementStore, IDocumentStore) are marked experimental (XAPI0001).
  • samples/LrsHost: a standalone LRS on SQLite or PostgreSQL, used as the target of the conformance runs. CI runs both batteries against it on both databases. samples/LrsHost/Dockerfile builds it into a non-root image that stores SQLite data in /data and will not start without credentials; CI builds and checks the image.
  • Samples EmbedInAspNetCore and FakeLrsInTests, and guides in docs/ (getting started, client, LRS, versions, migrating from TinCan.NET, conformance) whose C# blocks are compiled and checked by tests/Xapi.Documentation.Tests.
  • benchmarks/Xapi.Benchmarks: BenchmarkDotNet benchmarks for statement parsing, writing, validation and equivalence, outbox enqueueing, and the LRS statement endpoints on SQLite and PostgreSQL, with measured results in docs/benchmarks.md.
  • A documentation site built with docfx from the guides and the XML docs, deployed to GitHub Pages from main: https://nurullahturkoglu.github.io/xapi-dotnet/
  • Xapi.Lrs: MaxStatementsPerBatch (1,000 by default) limits the statements in one POST; larger batches get 413 before they are parsed.
  • Xapi.Lrs: MigrateXapiLrsDatabaseAsync() on the app's IServiceProvider applies the database migrations as a separate deployment step, so migrating no longer needs the experimental XapiLrsDbContext.
  • Xapi.Core: XapiErrorCodes.AttachmentPayloadMissing and XapiErrorCodes.AttachmentPayloadUnreferenced, the codes Xapi.Client and Xapi.Client.Outbox report for attachment data. The values are unchanged. XapiError.Path documents that the path of attachment.payload.unreferenced is an index into the attachment data (attachments[0]), not a JSON path.
  • Xapi.Lrs: AddXapiLrs(configuration, configure) binds the options from a configuration section, credentials included (Lrs:Credentials:0:Key, :Secret, :Scopes:0), and then runs configure for what only code can set. Scopes are xAPI scope names; a misspelt key, an incomplete credential, an unknown scope or a value out of range stops the host at startup, and no message contains a secret. samples/LrsHost reads its options this way, so it accepts more than one credential. See Options from configuration.
  • Xapi.Lrs: metrics on the Xapi.Lrs meter (xapi.lrs.request.duration, xapi.lrs.errors, xapi.lrs.statements.stored) and activities on the Xapi.Lrs activity source for storing and querying statements and writing documents, with System.Diagnostics and no OpenTelemetry dependency. Tags never carry agents, IRIs, ids or credential keys. The names are constants of XapiLrsDiagnostics. See Metrics and traces.
  • Xapi.Lrs: AddXapiLrsHealthCheck() reports whether the database can be reached and is migrated. samples/LrsHost serves it at /health.
  • Xapi.Client and Xapi.Client.Outbox: metrics on the Xapi.Client meter: statements sent, retried and dead-lettered, and the statements pending in outboxes. The netstandard2.0 build of Xapi.Client now depends on System.Diagnostics.DiagnosticSource.
  • Releases publish the samples/LrsHost image to ghcr.io as ghcr.io/nurullahturkoglu/xapi-lrs, for linux/amd64 and linux/arm64, tagged with the version and, for releases that are not previews, latest, with a signed build provenance attestation. See Running the sample LRS in Docker.
  • The packages have an icon, which the documentation site also uses as its logo and favicon.

Changed

  • Xapi.Core: reading a statement allocates about a quarter of what it did (5.2 KB instead of 19.7 KB for a 1 KB statement in strict mode) and takes about 40% less time. JSON paths are formatted only when an error is reported, and known property names are matched without creating strings or a hash set per object. Errors are unchanged. See docs/benchmarks.md.
  • Xapi.Lrs: statement queries read each filter's own index (verb, registration, and one range per agent or activity usage, plus StatementRef matches) newest first and only as far as one page, instead of testing every statement. At 100,000 statements a verb with no matches takes 0.22 ms instead of 70 ms on SQLite and 0.84 ms instead of 52 ms on PostgreSQL, and an agent query with related_agents 2.3 ms instead of 581 ms and 5.9 ms instead of 15 ms.
  • Xapi.Lrs: on SQLite, the matches through StatementRefs start from the filter's index when it matches fewer statements than there are StatementRefs, instead of looking up the target of every StatementRef. At 100,000 statements of which 10% are StatementRefs, a verb with no matches takes 0.42 ms instead of 23 ms and an agent query with related_agents 4.1 ms instead of 65 ms. PostgreSQL already chose this from its statistics. See docs/benchmarks.md.
  • Xapi.Lrs: attachments=true responses are streamed, reading the data of one attachment at a time, and HEAD reads none. IStatementStore.GetAttachmentsAsync is replaced by GetAttachmentSummariesAsync and GetAttachmentContentAsync.
  • Xapi.Lrs.Sqlite and `Xapi.Lrs.Postgre...
Read more