Peerpage.Gateway
1.2.0
dotnet add package Peerpage.Gateway --version 1.2.0
NuGet\Install-Package Peerpage.Gateway -Version 1.2.0
<PackageReference Include="Peerpage.Gateway" Version="1.2.0" />
<PackageVersion Include="Peerpage.Gateway" Version="1.2.0" />
<PackageReference Include="Peerpage.Gateway" />
paket add Peerpage.Gateway --version 1.2.0
#r "nuget: Peerpage.Gateway, 1.2.0"
#:package Peerpage.Gateway@1.2.0
#addin nuget:?package=Peerpage.Gateway&version=1.2.0
#tool nuget:?package=Peerpage.Gateway&version=1.2.0
Peerpage.Gateway — the customer's resident package
Trust model (read this first): this is the one Peerpage package that runs on
the customer's own servers, and it is the trust surface a hostile reviewer reads
line by line. It has zero external NuGet dependencies — WebSockets, JSON, and
HMAC-signed JWTs all come from the BCL; the only framework reference is
Microsoft.AspNetCore.App, the shared framework the host app already runs on. It
dials out only (one outbound WebSocket to the platform; the sole inbound
route is the same-origin identity-token mint). Nothing executable ever crosses
the socket — only invocation envelopes (function name + params), matched
against an allow-list loaded from the customer's own repo. There is no eval and
no dynamic code loading. The only SQL it ever issues is GRANT SELECT
(never a data read or write) to the read-only login peerpage_api_read_only,
derived solely from the committed Peerpage/reads/*.json files in your repo —
never from anything on the socket — and run through a DB connection you hand it
(o.CreateDbConnection); omit that option and it touches no database at all.
How a host wires it up
builder.Services.AddPeerpageGateway(o =>
{
o.PlatformUrl = cfg["Peerpage:PlatformUrl"]; // wss://… (/gateway is appended)
o.TenantId = cfg["Peerpage:TenantId"];
o.TenantKey = cfg["Peerpage:TenantKey"];
o.Registrations = Peerpage.Index.All; // the explicit menu, from peerpage/
// Makes approved reads go LIVE on deploy: at boot the gateway reads Peerpage/reads/*.json
// and applies the matching GRANT SELECT to peerpage_api_read_only, via a connection to YOUR
// database. Omit CreateDbConnection and it opens no DB connection (grants applied by a DBA).
o.CreateDbConnection = () => new SqlConnection(cfg.GetConnectionString("AppDb"));
o.Dialect = PeerpageDbDialect.SqlServer; // or .Postgres (e.g. a .NET + Postgres host)
o.GetUser = ctx => /* ClaimsPrincipal → new PeerpageUser(id, roles) */;
});
app.MapPeerpageIdentityEndpoint("/peerpage/token"); // inward-facing only; mints the 5-min JWT
What it does
- Outbound socket: connects to the platform, authenticates once at
Hellowith the tenant key, reports its loaded registrations upstream, heartbeats every 20s, and reconnects with jittered backoff. - Graceful rotation: before any platform request ceiling (e.g. Cloud Run's
60-minute cap) it announces
Draining, flushes replies, and reconnects — so the tenant keeps an unbroken line. - Read-grant reconcile (when
CreateDbConnectionis wired): once at boot (before it connects) and on a re-check nudge, it reconciles the committedPeerpage/reads/*.jsonfiles against your database —GRANT SELECTon the listed columns topeerpage_api_read_only, revoke any column no longer listed (a deleted file revokes its whole table). Idempotent, derived only from the committed files, and it's the gateway's only DDL. It attempts each grant and observes the result rather than pre-checking fordb_owner/superuser (a managed DB's table owner can grant without being either); if the app's DB principal genuinely can'tGRANT, it logs the honest waiting state and a DBA applies them. - Dispatch (allow-list): for each incoming call it rejects unknown functions, checks roles, validates params (strict typed deserialization + DataAnnotations — unknown/missing fields rejected), replays a cached result on a repeated idempotency key (so a retry never double-executes), then runs the customer's own handler in a fresh DI scope. Every call — accepted or rejected — is logged.
Registration shape
One file per capability. A registration declares its name, description, allowed
roles, and a Handle that calls the host's own service classes (never
reimplements them):
public sealed class FlagOrder : PeerpageFunction<FlagOrder.Params>
{
public record Params(int OrderId, [property: MaxLength(500)] string Reason);
public override string Name => "orders.flagOrder";
public override string Description => "Flag an order for review";
public override string[] Roles => ["admin", "superadmin"];
public override Task<object?> Handle(Params p, IServiceProvider sp) =>
sp.GetRequiredService<OrdersService>().FlagOrder(p.OrderId, p.Reason);
}
The wire protocol (Protocol/Wire.cs) is hand-mirrored from the zod schemas in
@peerpage/protocol; the language-agnostic conformance suite is the guard
against drift.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.