@reinconsole/graph 0.1.1 → 0.3.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Phase 3 of **[Rein](https://github.com/bugiiiii11/rein)** — the reputation graph. Guard receipts say what agents tried to spend; gate receipts say what vendors actually earned. The graph listens to both, scores every vendor and payer it has evidence on, and feeds the scores back into enforcement on both sides of the wire.
4
4
 
5
- > **Status: v0.1 — early open-source infrastructure, live on testnet.** APIs may change before 1.0. See the live scoreboard: [Rein console](https://app.reinconsole.com).
5
+ > **Status: v0.2 — early open-source infrastructure, live on testnet.** APIs may change before 1.0. See the live scoreboard: [Rein console](https://app.reinconsole.com).
6
6
 
7
7
  ## Install
8
8
 
@@ -31,7 +31,7 @@ createGate({ screen: { check: payerCheck(graph, { denyBelow: 40 }) }, /* ... */
31
31
  - **Unknown is not bad.** The evaluator never fires `vendorReputationLt` without data, `syncVendors` withholds scores under the confidence floor, and `payerCheck` passes wallets it knows nothing about. A newcomer is served; a *confidently* bad actor is refused.
32
32
  - **No-fault refusals carry no evidence.** Rate limits, velocity caps, and rails outages at one vendor's gate never bleed into a payer's global score.
33
33
  - **One identity, one history.** `graph.link(canonical, alias)` merges subjects across id spaces — an agent's engine ULID, its paying wallets, its on-chain [ERC-8004](https://www.npmjs.com/package/@reinconsole/erc8004) identity — so evidence follows the party, not the key. Key rotation never splits a score.
34
- - **Run it as a service.** `buildGraphServer()` — remote producers `POST /v1/events`, anyone reads `GET /v1/scores/:kind/:subject` and gets the score *and* the evidence behind it. The durable variant (evidence survives restarts) lives in the [monorepo](https://github.com/bugiiiii11/rein) (`@reinconsole/store`).
34
+ - **Run it as a service.** `buildGraphServer()` — remote producers `POST /v1/events`, anyone reads `GET /v1/scores/:kind/:subject` and gets the score *and* the evidence behind it. Pass `{ auth }` (an `ApiKeyAuth` from `@reinconsole/core/auth`, or set `REIN_GRAPH_API_KEY`) and the write routes demand a key holding the `report` scope while reads stay open; without one the server binds loopback and refuses a public bind. The durable variant (evidence survives restarts) lives in the [monorepo](https://github.com/bugiiiii11/rein) (`@reinconsole/store`).
35
35
 
36
36
  Scoring weights are deliberately transparent v0.1 heuristics (`DEFAULT_WEIGHTS`, overridable) — scores are pure functions of the evidence ledger, so the model can evolve without migrations.
37
37
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ReputationSubject, ReputationComponents, ReinEvent, ReputationScore, Receipt } from '@reinconsole/core';
2
2
  import { FastifyInstance } from 'fastify';
3
+ import { ApiKeyAuth } from '@reinconsole/core/auth';
3
4
 
4
5
  /** A value that may be produced synchronously or awaited (durable writes). */
5
6
  type MaybePromise<T> = T | Promise<T>;
@@ -186,8 +187,21 @@ declare function reliabilityComponent(ev: SubjectEvidence): number;
186
187
  * Confidence is first-class (see core ReputationScore): a thin or brand-new
187
188
  * history yields LOW confidence rather than a misleading score, so consumers
188
189
  * (engine sync, gate screening) can refuse to act on it. Depth of evidence
189
- * saturates around 10 observations; age discounts same-day evidence to 40%
190
- * and stops mattering after a week.
190
+ * saturates around 10 observations; age stops mattering after a week.
191
+ *
192
+ * The 0.1 age floor is load-bearing for fairness. Both consumers act at a 0.3
193
+ * confidence floor (`syncVendors`, `payerCheck`), so capping a day-0 subject at
194
+ * 0.1 makes first-day enforcement impossible BY CONSTRUCTION rather than by
195
+ * arithmetic luck. It used to be 0.4, which left day-0 subjects enforceable
196
+ * after only 14 observations — `depth` saturates so fast that a busy vendor
197
+ * cleared that in seconds, and the console's own vendor sat at 0.30136 against
198
+ * the 0.3 floor, i.e. inside the newcomer grace period by 0.0014. Enforcement
199
+ * now needs roughly 1.6 days (high volume) to 2.3 days (moderate) of history:
200
+ * a grace period measured in time, which is what "newcomer" means.
201
+ *
202
+ * Note the constant only ever applies to subjects younger than a week — past
203
+ * 7 days `min(1, d/7)` saturates and age is 1.0 under any floor — so lowering
204
+ * it leaves every mature score bit-for-bit unchanged.
191
205
  */
192
206
  declare function confidence(ev: SubjectEvidence, nowMs: number): number;
193
207
  /** Blend all five components into the headline 0–100 score. */
@@ -364,11 +378,50 @@ interface PayerCheckOptions {
364
378
  */
365
379
  declare function payerCheck(graph: ReputationGraph, options?: PayerCheckOptions): (payer: string) => string | undefined;
366
380
 
381
+ interface GraphServerOptions {
382
+ /**
383
+ * API-key authentication for the WRITE routes. Omit for embedded use (the
384
+ * console world holds the graph object directly) or for a loopback-only
385
+ * process; pass an {@link ApiKeyAuth} and every write demands a `report`
386
+ * scope while reads stay open.
387
+ */
388
+ auth?: ApiKeyAuth;
389
+ }
367
390
  /**
368
391
  * Build the graph HTTP API. Remote producers POST their events here; anyone
369
392
  * can read scores with the evidence behind them. Pass a graph for tests, or
370
393
  * let it create a fresh in-memory one.
394
+ *
395
+ * READS ARE OPEN BY DESIGN and that is the whole point of a reputation graph:
396
+ * a score nobody can read governs nothing. WRITES are the asymmetry — an event
397
+ * posted here becomes evidence about a subject that did not post it — so when
398
+ * `auth` is supplied they require the `report` scope. Without `auth` the server
399
+ * is exactly as open as it always was, which is why the bins that expose it
400
+ * refuse a public bind unless a key exists or the operator says otherwise.
371
401
  */
372
- declare function buildGraphServer(graph?: ReputationGraph): FastifyInstance;
402
+ declare function buildGraphServer(graph?: ReputationGraph, options?: GraphServerOptions): FastifyInstance;
403
+ /**
404
+ * Build the graph's auth layer from the environment, or explain why there is
405
+ * none. `REIN_GRAPH_API_KEY` seeds one or more comma-separated secrets.
406
+ *
407
+ * They are seeded with `report` and nothing else: the graph has no key-issuing
408
+ * routes to bootstrap (unlike the engine, which seeds `admin` so `/v1/keys` is
409
+ * reachable), so an `admin` secret here would grant authority no route needs.
410
+ */
411
+ declare function graphAuthFromEnv(env: NodeJS.ProcessEnv): Promise<ApiKeyAuth | undefined>;
412
+ /**
413
+ * Where a graph process binds, given whether auth exists.
414
+ *
415
+ * Until D1(a) the graph had no key to trade, so the only trade available was a
416
+ * statement: `REIN_GRAPH_PUBLIC=1`. Now that writes can demand a `report` key,
417
+ * the rule matches the engine's — a key buys a public bind, and asking for one
418
+ * without a key is a startup ERROR rather than a warning. `REIN_GRAPH_PUBLIC=1`
419
+ * survives as the deliberate override for an open graph, because a read-only
420
+ * showcase is a real deployment and reads were never the exposure.
421
+ */
422
+ declare function resolveGraphHost(env: NodeJS.ProcessEnv, hasAuth?: boolean): {
423
+ host: string;
424
+ warning?: string;
425
+ };
373
426
 
374
- export { type CounterpartyLine, DEFAULT_CORRELATION_LIMIT, DEFAULT_WEIGHTS, type EventSource, EvidenceLedger, type EvidenceLedgerPort, InMemoryIntentStore, type IntentCorrelationPort, type IntentFacts, type ManualReport, type MaybePromise, type PayerCheckOptions, type ReputationExplanation, ReputationGraph, type ReputationGraphOptions, type ScoreWeights, type SubjectEvidence, type SyncOptions, type SyncedVendorScore, type VendorReputationSink, blend, blendBase, buildGraphServer, confidence, disputeComponent, longevityComponent, normalizeSubject, payerCheck, reliabilityComponent, subjectKey, volumeComponent };
427
+ export { type CounterpartyLine, DEFAULT_CORRELATION_LIMIT, DEFAULT_WEIGHTS, type EventSource, EvidenceLedger, type EvidenceLedgerPort, type GraphServerOptions, InMemoryIntentStore, type IntentCorrelationPort, type IntentFacts, type ManualReport, type MaybePromise, type PayerCheckOptions, type ReputationExplanation, ReputationGraph, type ReputationGraphOptions, type ScoreWeights, type SubjectEvidence, type SyncOptions, type SyncedVendorScore, type VendorReputationSink, blend, blendBase, buildGraphServer, confidence, disputeComponent, graphAuthFromEnv, longevityComponent, normalizeSubject, payerCheck, reliabilityComponent, resolveGraphHost, subjectKey, volumeComponent };