@gvnrdao/dh-sdk 0.0.309 → 0.0.312

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.
@@ -39,18 +39,62 @@ export interface ServerSessionStore {
39
39
  load(key: string): PersistedServerSession | null;
40
40
  save(key: string, session: PersistedServerSession): void;
41
41
  clear(key: string): void;
42
+ /**
43
+ * Optional pending-envelope persistence (see `PendingLoginEnvelope`). Optional
44
+ * so a custom store written before this existed still satisfies the interface;
45
+ * without it the session simply mints a fresh envelope each attempt, which is
46
+ * the pre-existing behavior and only costs a multi-owner Safe a restart.
47
+ */
48
+ loadPending?(key: string): PendingLoginEnvelope | null;
49
+ savePending?(key: string, envelope: PendingLoginEnvelope): void;
50
+ clearPending?(key: string): void;
42
51
  }
43
52
  export declare function sessionStoreKey(address: string, chainId: number, endpoint: string): string;
53
+ /**
54
+ * The UNSIGNED envelope of a login already collecting signatures.
55
+ *
56
+ * A multi-owner Safe signs across sessions — owner 1 now, owner 2 later — and
57
+ * identifies the pending message solely by its EIP-712 hash. Every field is
58
+ * hashed, so minting a fresh `nonce`/`issuedAt` for the next attempt yields a
59
+ * DIFFERENT hash, which Safe presents as a new message needing its FIRST
60
+ * signature. Whatever owner 1 signed is orphaned, and a 2-of-N Safe can never
61
+ * reach threshold: every retry resets the count to zero.
62
+ *
63
+ * Reusing the envelope makes the re-request hash identically, so Safe shows the
64
+ * same pending message and the next owner ADDS to it. Invisible for an EOA,
65
+ * which signs immediately and spends the envelope.
66
+ */
67
+ export interface PendingLoginEnvelope {
68
+ issuedAt: number;
69
+ nonce: string;
70
+ }
71
+ /**
72
+ * How long an unsigned envelope stays reusable. Must stay UNDER the server's
73
+ * contract-wallet first-use skew (`CONTRACT_ISSUED_AT_SKEW_SECONDS`, 10 min):
74
+ * beyond it the first use is rejected as stale, so reuse would guarantee failure
75
+ * instead of preventing a re-sign.
76
+ */
77
+ export declare const PENDING_ENVELOPE_TTL_SECONDS: number;
78
+ /** Storage key for the pending envelope matching a session key. */
79
+ export declare function pendingEnvelopeKey(sessionKey: string): string;
80
+ export declare function isPendingEnvelopeShape(value: any): value is PendingLoginEnvelope;
44
81
  export declare class LocalStorageSessionStore implements ServerSessionStore {
45
82
  load(key: string): PersistedServerSession | null;
46
83
  save(key: string, session: PersistedServerSession): void;
47
84
  clear(key: string): void;
85
+ loadPending(key: string): PendingLoginEnvelope | null;
86
+ savePending(key: string, envelope: PendingLoginEnvelope): void;
87
+ clearPending(key: string): void;
48
88
  }
49
89
  export declare class MemorySessionStore implements ServerSessionStore {
50
90
  private readonly entries;
91
+ private readonly pending;
51
92
  load(key: string): PersistedServerSession | null;
52
93
  save(key: string, session: PersistedServerSession): void;
53
94
  clear(key: string): void;
95
+ loadPending(key: string): PendingLoginEnvelope | null;
96
+ savePending(key: string, envelope: PendingLoginEnvelope): void;
97
+ clearPending(key: string): void;
54
98
  }
55
99
  /** `localStorage` when usable (browser, not blocked), else per-instance memory. */
56
100
  export declare function createDefaultSessionStore(): ServerSessionStore;
@@ -22,6 +22,7 @@
22
22
  * `~/.diamond-hands/session.json` for cross-invocation reuse.
23
23
  */
24
24
  import type { Signer } from "ethers";
25
+ import { type DhServerLoginPayload } from "./eip712-login";
25
26
  import { type ServerSessionStore } from "./server-session-store";
26
27
  export interface ServerSessionOptions {
27
28
  signer: Signer;
@@ -34,12 +35,28 @@ export interface ServerSessionOptions {
34
35
  sessionStore?: ServerSessionStore;
35
36
  /** Set false to disable persistence entirely (fresh signature per instance + expiry). */
36
37
  persistSession?: boolean;
38
+ /**
39
+ * Audit M-8 audience to bind the login to. Defaults to `serviceEndpoint`.
40
+ *
41
+ * A caller that shares ONE signature across several services (the browser
42
+ * frontend, which authenticates both lit-ops-server and the api from a single
43
+ * wallet prompt) cannot bind to a single endpoint, so it passes the app audience
44
+ * instead — and every server it talks to must accept that value. Node callers
45
+ * (CLI, MCP, cr-monitor) omit it and keep the endpoint binding unchanged.
46
+ */
47
+ loginAudience?: string;
37
48
  /**
38
49
  * Fires immediately before a wallet signature is requested — i.e. only when
39
50
  * neither the cached JWT nor the persisted envelope could renew the session,
40
51
  * never on the silent paths. Lets UIs show a "check your wallet" prompt.
41
52
  */
42
53
  onSignaturePrompt?: () => void;
54
+ /**
55
+ * Fires once the prompted signature has concluded, with whether a session was
56
+ * established. Without it `onSignaturePrompt` has no ending, so a UI showing
57
+ * "check your wallet" can never take it down.
58
+ */
59
+ onSignatureResolved?: (ok: boolean) => void;
43
60
  /** Override for tests. */
44
61
  now?: () => number;
45
62
  /** Override for tests. */
@@ -53,9 +70,11 @@ export declare class ServerLoginError extends Error {
53
70
  export declare class ServerSession {
54
71
  private readonly signer;
55
72
  private readonly serviceEndpoint;
73
+ private readonly loginAudience;
56
74
  private readonly chainId;
57
75
  private readonly store;
58
76
  private readonly onSignaturePrompt?;
77
+ private readonly onSignatureResolved?;
59
78
  private readonly now;
60
79
  private readonly fetchImpl;
61
80
  private cached;
@@ -111,6 +130,24 @@ export declare class ServerSession {
111
130
  * of server response so the client stops presenting the token.
112
131
  */
113
132
  logout(): Promise<void>;
133
+ /**
134
+ * Adopt a login envelope signed ELSEWHERE and exchange it for a session now.
135
+ *
136
+ * The browser frontend collects one wallet signature and spends it at both the
137
+ * api and this service, so the envelope is minted outside the SDK. Without this
138
+ * seam the SDK would mint its own — a second wallet prompt, which for a
139
+ * multi-sig Safe is another full propose-and-confirm ceremony across owners.
140
+ *
141
+ * Must be called PROMPTLY after signing. Each server pins the nonce on FIRST
142
+ * use and requires that first use to be fresh (±120s EOA, ±600s contract
143
+ * wallet); the stores are independent, so a late hand-off is rejected as
144
+ * `stale_first_use` even though the other service already accepted it. After a
145
+ * successful first use, silent re-mint covers the next 24h.
146
+ *
147
+ * No-ops the prompt path entirely: on success the session is cached and
148
+ * persisted exactly as a self-minted login would be.
149
+ */
150
+ adoptLoginPayload(payload: DhServerLoginPayload): Promise<void>;
114
151
  /**
115
152
  * Resolve a session WITHOUT ever prompting for a wallet signature: a live
116
153
  * cached token, else the persisted JWT (adopted if still fresh, otherwise
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gvnrdao/dh-sdk",
3
- "version": "0.0.309",
3
+ "version": "0.0.312",
4
4
  "description": "TypeScript SDK for Diamond Hands Protocol - Bitcoin-backed lending with LIT Protocol PKPs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",