feat(#26): Nostr identity + trust engine
- New nostr_identities DB table (pubkey, trust_score, tier, interaction_count, sats_absorbed_today, last_seen) - nullable nostr_pubkey FK on sessions + jobs tables; schema pushed - TrustService: getTier, getOrCreate, recordSuccess/Failure, HMAC token (issue/verify) - Soft score decay (lazy, on read) when identity absent > N days - POST /api/identity/challenge + POST /api/identity/verify (NIP-01 sig verification) - GET /api/identity/me — look up trust profile by X-Nostr-Token - POST /api/sessions + POST /api/jobs accept optional nostr_token; bind pubkey to row - GET /sessions/:id + GET /jobs/:id include trust_tier in response - recordSuccess/Failure called after session request + job work completes - X-Nostr-Token added to CORS allowedHeaders + exposedHeaders - TIMMY_TOKEN_SECRET set as persistent shared env var
This commit is contained in:
@@ -5,3 +5,4 @@ export * from "./messages";
|
||||
export * from "./bootstrap-jobs";
|
||||
export * from "./world-events";
|
||||
export * from "./sessions";
|
||||
export * from "./nostr-identities";
|
||||
|
||||
@@ -36,6 +36,9 @@ export const jobs = pgTable("jobs", {
|
||||
actualOutputTokens: integer("actual_output_tokens"),
|
||||
actualCostUsd: real("actual_cost_usd"),
|
||||
|
||||
// Optional Nostr identity bound at job creation
|
||||
nostrPubkey: text("nostr_pubkey"),
|
||||
|
||||
// ── Post-work honest accounting & refund ─────────────────────────────────
|
||||
actualAmountSats: integer("actual_amount_sats"),
|
||||
refundAmountSats: integer("refund_amount_sats"),
|
||||
|
||||
39
lib/db/src/schema/nostr-identities.ts
Normal file
39
lib/db/src/schema/nostr-identities.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { pgTable, text, timestamp, integer } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
// ── Trust tier labels ─────────────────────────────────────────────────────────
|
||||
// Boundaries are env-var overridable via TrustService.
|
||||
|
||||
export const TRUST_TIERS = ["new", "established", "trusted", "elite"] as const;
|
||||
export type TrustTier = (typeof TRUST_TIERS)[number];
|
||||
|
||||
// ── nostr_identities ──────────────────────────────────────────────────────────
|
||||
// One row per Nostr pubkey (64-char lowercase hex). Trust score drives pricing
|
||||
// decisions in the cost-routing layer (Task #27).
|
||||
|
||||
export const nostrIdentities = pgTable("nostr_identities", {
|
||||
pubkey: text("pubkey").primaryKey(),
|
||||
|
||||
trustScore: integer("trust_score").notNull().default(0),
|
||||
tier: text("tier").$type<TrustTier>().notNull().default("new"),
|
||||
interactionCount: integer("interaction_count").notNull().default(0),
|
||||
|
||||
// Rolling daily absorption budget (reset by TrustService on read)
|
||||
satsAbsorbedToday: integer("sats_absorbed_today").notNull().default(0),
|
||||
absorbedResetAt: timestamp("absorbed_reset_at", { withTimezone: true })
|
||||
.defaultNow()
|
||||
.notNull(),
|
||||
|
||||
lastSeen: timestamp("last_seen", { withTimezone: true }).defaultNow().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const insertNostrIdentitySchema = createInsertSchema(nostrIdentities).omit({
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
});
|
||||
|
||||
export type NostrIdentity = typeof nostrIdentities.$inferSelect;
|
||||
export type InsertNostrIdentity = z.infer<typeof insertNostrIdentitySchema>;
|
||||
@@ -43,6 +43,9 @@ export const sessions = pgTable("sessions", {
|
||||
// Auth token — issued once when session activates; required for requests
|
||||
macaroon: text("macaroon"),
|
||||
|
||||
// Optional Nostr identity bound at session creation
|
||||
nostrPubkey: text("nostr_pubkey"),
|
||||
|
||||
// TTL — refreshed on each successful request
|
||||
expiresAt: timestamp("expires_at", { withTimezone: true }),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user