Single-file .NET SDK for the Shre AI / AROS platform. Zero external dependencies (System.* only). Targets net6.0 and net8.0.
Spec: SHARED-SDK-SPEC.md Sibling SDKs: Python, Swift, Kotlin
using ShreAI;
var sa = ShreClient.Initialize(new ShreConfig
{
Endpoint = "https://apiauth.shre.ai",
EventsEndpoint = "https://events.shre.ai",
TenantId = "merchant_123",
App = "rapid_pos",
Mode = ShreMode.ReadOnly,
});
await sa.StartAsync();
ShreClient.Track("price_updated", entityType: "item", entityId: "UPC_012345678905",
metadata: new Dictionary<string, object?> { ["old"] = 10.49, ["new"] = 10.99 });
// On shutdown:
await sa.StopAsync();dotnet add package ShreAI --version 2.0.0git submodule add https://github.com/Shreai/sdk-dotnet third_party/shreai-dotnetReference the project in your .csproj:
<ProjectReference Include="..\third_party\shreai-dotnet\src\ShreAI\ShreAI.csproj" />The SDK is one file: src/ShreAI/ShreAI.cs. Copy it into your project. No csproj changes required beyond targeting net6.0+.
using ShreAI;
internal class Program
{
static async Task Main()
{
await using var sa = new ShreClient(new ShreConfig
{
Endpoint = "https://apiauth.shre.ai",
EventsEndpoint = "https://events.shre.ai",
TenantId = "merchant_123",
App = "rapid_pos",
Mode = ShreMode.ReadOnly,
StoreId = "store_42",
UserId = "user_1",
Role = "manager",
OnError = (ex, ctx) => Console.Error.WriteLine($"[shre:{ctx}] {ex.Message}"),
OnFlush = (ok, ko) => Console.WriteLine($"[shre] flushed ok={ok} rejected={ko}"),
});
await sa.StartAsync();
sa.TrackEvent("app_started");
// ...
await sa.StopAsync();
}
}public sealed class ShreHostedService : IHostedService
{
private readonly ShreClient _client;
public ShreHostedService(IConfiguration cfg)
{
_client = ShreClient.Initialize(new ShreConfig
{
Endpoint = cfg["ShreAI:Endpoint"] ?? "https://apiauth.shre.ai",
EventsEndpoint = cfg["ShreAI:EventsEndpoint"] ?? "https://events.shre.ai",
TenantId = cfg["ShreAI:TenantId"]!,
App = cfg["ShreAI:App"]!,
Mode = ShreMode.ReadOnly,
});
}
public Task StartAsync(CancellationToken ct) => _client.StartAsync(ct);
public Task StopAsync(CancellationToken ct) => _client.StopAsync(ct);
}
// Program.cs
builder.Services.AddHostedService<ShreHostedService>();After this, anywhere in your app:
ShreClient.Track("order_placed", entityType: "order", entityId: orderId,
metadata: new Dictionary<string, object?> { ["total"] = order.Total });The same IHostedService pattern works for Worker Services. Inject the singleton and call the static ShreClient.Track(...) from your BackgroundService.
| Field | Default | Description |
|---|---|---|
Endpoint |
required | Control plane (https://apiauth.shre.ai) |
EventsEndpoint |
falls back to Endpoint |
Data plane (https://events.shre.ai) |
TenantId |
required | Merchant / tenant identifier |
App |
required | App slug; must match ^[a-z][a-z0-9_-]{0,31}$ |
Mode |
ShreMode.ReadOnly |
ReadOnly (analytics) or ReadWrite (workflow agent) |
BootstrapKey |
required for ReadWrite |
Server-issued bootstrap key |
StoreId / UserId / Role |
null |
Optional context surfaced in events |
SdkVersion |
"dotnet/2.0.0" |
Sent as X-Shre-SDK-Version and User-Agent |
FlushIntervalSeconds |
10 |
Periodic flush cadence (server can override via nextFlushSeconds) |
BatchSize |
50 |
Max events per flush |
MaxQueueSize |
5000 |
Bounded queue; oldest events drop when full |
TimeoutMs |
8000 |
HTTP timeout |
OnError |
null |
(Exception, string contextTag) => void |
OnFlush |
null |
(int accepted, int rejected) => void |
// Minimal
ShreClient.Track("screen_viewed");
// Full
ShreClient.Track(
name: "price_updated",
entityType: "item",
entityId: "UPC_012345678905",
metadata: new Dictionary<string, object?>
{
["old_price"] = 10.49,
["new_price"] = 10.99,
["changed_by"] = "user_42",
});
// Manual flush (rare — periodic timer handles this)
var ack = await sa.FlushAsync();
Console.WriteLine($"accepted={ack.Accepted} rejected={ack.Rejected}");
// Heartbeat (optional, surfaces SDK presence + queue depth)
await sa.HeartbeatAsync(deviceId: Environment.MachineName);| HTTP | SDK behavior |
|---|---|
200 |
Batch accepted; reset retry counter; honor nextFlushSeconds if returned |
401 |
Re-bootstrap (refresh sdkToken); requeue batch |
403 |
Kill switch — trackingEnabled flips to false; no further posts |
429 / 5xx |
Exponential backoff (5s → 15s → 30s → 60s → 300s); requeue batch |
| Network | Requeue batch; backoff; events stay in memory until MaxQueueSize is hit |
| Timeout | Same as network |
OnError(Exception, string) fires for every non-recoverable error with a context tag ("flush", "bootstrap", "flush-loop", "config-loop").
OnFlush(int accepted, int rejected) fires after every flush attempt — successful or not.
- No
Microsoft.Extensions.Loggingdependency. This SDK isSystem.*only. Wire toILoggerviaOnError/OnFlushdelegates:OnError = (ex, ctx) => logger.LogError(ex, "ShreAI {Context}", ctx), OnFlush = (ok, ko) => logger.LogInformation("ShreAI flush ok={Ok} ko={Ko}", ok, ko),
IDisposableandIAsyncDisposable. Either works; preferawait usingin async contexts to avoid blocking onStopAsync.AppDomain.ProcessExitdrains queued events at shutdown. ASP.NET Core's graceful shutdown also callsIHostedService.StopAsyncwhich callsStopAsync()directly — the SDK is safe under both paths.- Thread safety. All public methods are safe to call from any thread. The internal queue is a
ConcurrentQueue<T>; flush serialization uses aSemaphoreSlim. - JSON. Uses
System.Text.JsonwithCamelCasepolicy. No Newtonsoft dependency. - Trimming / AOT. The SDK uses
Dictionary<string, object?>for event metadata which relies on reflection-based serialization. For full AOT compatibility, pin metadata to typed records or setJsonSerializerIsReflectionEnabledByDefault=truein your.csproj.
- Endpoints validated at construction;
downloads.*hostnames are rejected (those are weight-distribution hosts, not data planes). ReadWritemode requires a server-issuedBootstrapKey;ReadOnlydoes not.- All requests carry
X-Shre-Tenant,X-Shre-App,X-Shre-SDK-Versionheaders and (after bootstrap)Authorization: Bearer <sdkToken>. - Events carry stable
eventIdUUIDs for server-side idempotency. - The control plane can disable tracking at any moment via the
trackingEnabledflag; the SDK respects it on the next flush or config refresh (every 5 minutes).
This SDK follows the <lang>/<major.minor.patch> scheme used by all Shre AI SDKs. The SdkVersion string sent on every request is dotnet/2.0.0 for this release.
MIT — see LICENSE.