Peerpage.Gateway 1.2.0

dotnet add package Peerpage.Gateway --version 1.2.0
                    
NuGet\Install-Package Peerpage.Gateway -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Peerpage.Gateway" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Peerpage.Gateway" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Peerpage.Gateway" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Peerpage.Gateway --version 1.2.0
                    
#r "nuget: Peerpage.Gateway, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Peerpage.Gateway@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Peerpage.Gateway&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Peerpage.Gateway&version=1.2.0
                    
Install as a Cake Tool

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 Hello with 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 CreateDbConnection is wired): once at boot (before it connects) and on a re-check nudge, it reconciles the committed Peerpage/reads/*.json files against your database — GRANT SELECT on the listed columns to peerpage_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 for db_owner/superuser (a managed DB's table owner can grant without being either); if the app's DB principal genuinely can't GRANT, 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.

Version Downloads Last Updated
1.2.0 99 9/3/2026
1.1.5 136 8/20/2026
1.1.4 112 8/19/2026
1.1.3 98 8/19/2026
1.1.1 99 8/10/2026
1.1.0 95 8/10/2026