- 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
40 lines
1.9 KiB
TypeScript
40 lines
1.9 KiB
TypeScript
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>;
|