@northstartech/cipher 0.0.0-stage → 1.0.0

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.
Files changed (41) hide show
  1. package/README.md +541 -2
  2. package/dist/client.d.ts +237 -0
  3. package/dist/client.js +555 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/crypto.d.ts +72 -0
  6. package/dist/crypto.js +244 -0
  7. package/dist/crypto.js.map +1 -0
  8. package/dist/encoding.d.ts +17 -0
  9. package/dist/encoding.js +79 -0
  10. package/dist/encoding.js.map +1 -0
  11. package/dist/errors.d.ts +14 -0
  12. package/dist/errors.js +22 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.js +6 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/integrations/drizzle.d.ts +96 -0
  18. package/dist/integrations/drizzle.js +285 -0
  19. package/dist/integrations/drizzle.js.map +1 -0
  20. package/dist/integrations/mongoose.d.ts +72 -0
  21. package/dist/integrations/mongoose.js +582 -0
  22. package/dist/integrations/mongoose.js.map +1 -0
  23. package/dist/integrations/prisma.d.ts +65 -0
  24. package/dist/integrations/prisma.js +588 -0
  25. package/dist/integrations/prisma.js.map +1 -0
  26. package/dist/integrations/sequelize.d.ts +62 -0
  27. package/dist/integrations/sequelize.js +677 -0
  28. package/dist/integrations/sequelize.js.map +1 -0
  29. package/dist/integrations/shared.d.ts +268 -0
  30. package/dist/integrations/shared.js +513 -0
  31. package/dist/integrations/shared.js.map +1 -0
  32. package/dist/integrations/typeorm.d.ts +109 -0
  33. package/dist/integrations/typeorm.js +457 -0
  34. package/dist/integrations/typeorm.js.map +1 -0
  35. package/dist/object.d.ts +53 -0
  36. package/dist/object.js +226 -0
  37. package/dist/object.js.map +1 -0
  38. package/dist/search.d.ts +250 -0
  39. package/dist/search.js +258 -0
  40. package/dist/search.js.map +1 -0
  41. package/package.json +101 -4
@@ -0,0 +1,237 @@
1
+ import { CipherError } from './errors.js';
2
+ import { EncryptedObject } from './object.js';
3
+ import { SearchIndex, type SearchIndexInfo } from './search.js';
4
+ /** Object identity bound into every ciphertext. `collection` and `field` are required. */
5
+ export interface CipherContext {
6
+ collection: string;
7
+ field: string;
8
+ recordId?: string | number;
9
+ [key: string]: string | number | undefined;
10
+ }
11
+ export type Normalization = 'exact' | 'case_insensitive' | 'email' | 'alphanumeric' | 'digits';
12
+ export interface WireEvent {
13
+ direction: 'request' | 'response';
14
+ method: string;
15
+ path: string;
16
+ status?: number;
17
+ body: string;
18
+ }
19
+ export interface NorthstarCipherOptions {
20
+ /** Gateway URL, e.g. `https://api.northstar.mx` (or `http://localhost:7080` locally). */
21
+ endpoint?: string;
22
+ /** Workload credential (`nsc_dev_…` / `nsc_live_…`). */
23
+ credential: string;
24
+ /** Default key alias. Default `"default"`. */
25
+ keyId?: string;
26
+ /** Attestation policy. `auto` = development for `nsc_dev_`, production for `nsc_live_`. */
27
+ attestation?: 'auto' | 'development' | 'production';
28
+ /** Pinned verifier keys, `"<kid>:<base64url public key>"`. Required for production. */
29
+ trustAnchors?: string[];
30
+ /** Optional allow-list of enclave measurements. */
31
+ allowedMeasurements?: string[];
32
+ /** Request timeout. Default 30 000 ms. */
33
+ timeoutMs?: number;
34
+ /** Retries for retryable failures. Default 2. */
35
+ maxRetries?: number;
36
+ /** Coalesce concurrent `encrypt`/`decrypt` calls into batch requests. Default on. */
37
+ batching?: boolean | {
38
+ maxItems?: number;
39
+ };
40
+ /** Observe the sealed traffic exactly as the gateway sees it (diagnostics, playground). */
41
+ debug?: {
42
+ onWire?: (e: WireEvent) => void;
43
+ };
44
+ /** Custom fetch (tests, proxies). */
45
+ fetch?: typeof fetch;
46
+ }
47
+ export interface EncryptInput {
48
+ keyId?: string;
49
+ plaintext: string | Uint8Array;
50
+ context: CipherContext;
51
+ /** Generate a blind index for exact-match search (requires `cipher.index`). */
52
+ index?: boolean | {
53
+ normalization?: Normalization;
54
+ };
55
+ }
56
+ export interface EncryptOutput {
57
+ ciphertext: string;
58
+ keyId: string;
59
+ keyVersion: number;
60
+ /** Blind index token when requested. */
61
+ index?: string;
62
+ }
63
+ export interface DecryptInput {
64
+ ciphertext: string;
65
+ context: CipherContext;
66
+ purpose?: string;
67
+ }
68
+ export interface DecryptOutput {
69
+ plaintext: string;
70
+ bytes: Uint8Array;
71
+ keyId: string;
72
+ keyVersion: number;
73
+ }
74
+ export interface EncryptObjectInput {
75
+ /** Key alias or id that protects the object key. Default: the client's key. */
76
+ keyId?: string;
77
+ plaintext: string | Uint8Array;
78
+ /** Where the wrapped key is stored: `collection`, `field` and `recordId`. */
79
+ context: CipherContext;
80
+ /** log2 of the block size, 12 … 24. Default 16 (64 KiB). */
81
+ blockLog2?: number;
82
+ }
83
+ export interface DecryptObjectInput {
84
+ /** The NSOB v1 object from `encryptObject()`. */
85
+ object: Uint8Array;
86
+ /** The `nsc:v1:` wrapped key stored with the record. */
87
+ wrappedKey: string;
88
+ context: CipherContext;
89
+ purpose?: string;
90
+ }
91
+ export interface SessionDetails {
92
+ instanceId: string;
93
+ platform: 'simulated' | 'sev-snp';
94
+ measurement: string;
95
+ sessionId: string;
96
+ expiresAt: Date;
97
+ scopes: string[];
98
+ }
99
+ export type Settled<T> = {
100
+ ok: true;
101
+ value: T;
102
+ } | {
103
+ ok: false;
104
+ error: CipherError;
105
+ };
106
+ /** Normalize a context: camelCase → snake_case keys, values to strings, drop undefined. */
107
+ export declare function wireContext(ctx: CipherContext): Record<string, string>;
108
+ /**
109
+ * Northstar Cipher client. Create one per process and share it.
110
+ *
111
+ * ```ts
112
+ * const cipher = new NorthstarCipher({ endpoint: process.env.NORTHSTAR_CIPHER_ENDPOINT, credential: process.env.NORTHSTAR_CIPHER_CREDENTIAL! })
113
+ * const { ciphertext } = await cipher.encrypt({ plaintext: 'G12345678', context: { collection: 'citizens', field: 'passport', recordId: '123' } })
114
+ * ```
115
+ */
116
+ export declare class NorthstarCipher {
117
+ private readonly endpoint;
118
+ private readonly keyId;
119
+ private readonly opts;
120
+ private readonly fetchImpl;
121
+ private credential?;
122
+ private token?;
123
+ private tokenInFlight?;
124
+ private sessionValue?;
125
+ private sessionInFlight?;
126
+ private trustCache;
127
+ private encryptQueue;
128
+ private decryptQueue;
129
+ private flushScheduled;
130
+ private warnedBatch;
131
+ constructor(opts: NorthstarCipherOptions);
132
+ /** Create a client from `NORTHSTAR_CIPHER_*` environment variables (Node.js). */
133
+ static fromEnv(overrides?: Partial<NorthstarCipherOptions>): NorthstarCipher;
134
+ private cred;
135
+ private policy;
136
+ private wire;
137
+ private http;
138
+ private plain;
139
+ private getToken;
140
+ private verifierKey;
141
+ private getSession;
142
+ private callOnce;
143
+ private call;
144
+ private get batchMax();
145
+ private schedule;
146
+ private flush;
147
+ private runEncrypt;
148
+ private runDecrypt;
149
+ private batchingEnabled;
150
+ /** Encrypt one value. Concurrent calls are coalesced into batch requests. */
151
+ encrypt(input: EncryptInput): Promise<EncryptOutput>;
152
+ private encryptDirect;
153
+ private encryptItem;
154
+ /** Decrypt one value. Concurrent calls are coalesced into batch requests when permitted. */
155
+ decrypt(input: DecryptInput): Promise<DecryptOutput>;
156
+ private decryptDirect;
157
+ /** One `/v1/decrypt`, returning the plaintext as bytes only. */
158
+ private decryptBytes;
159
+ /**
160
+ * Encrypt an object of any size (protocol §13): a random 32-byte object key
161
+ * seals the bytes here (NSOB v1, AES-256-GCM blocks) and Cipher encrypts only
162
+ * that key for `context`, in one `/v1/encrypt`. The object never leaves this process.
163
+ *
164
+ * ```ts
165
+ * const context = { collection: 'documents', field: 'file_key', recordId: doc.id }
166
+ * const { wrappedKey, object } = await cipher.encryptObject({ plaintext: pdf, context })
167
+ * ```
168
+ */
169
+ encryptObject(input: EncryptObjectInput): Promise<EncryptedObject>;
170
+ /**
171
+ * Decrypt an object sealed by `encryptObject()`: Cipher decrypts its key for
172
+ * `context` in one `/v1/decrypt` (policy, purpose and audit as for any decrypt),
173
+ * then the bytes are opened here. Any tampering raises `INVALID_CIPHERTEXT`.
174
+ */
175
+ decryptObject(input: DecryptObjectInput): Promise<Uint8Array>;
176
+ /** Encrypt many values; throws the first item error. */
177
+ encryptMany(items: EncryptInput[]): Promise<EncryptOutput[]>;
178
+ /** Encrypt many values, reporting per-item outcomes. */
179
+ encryptManySettled(items: EncryptInput[]): Promise<Settled<EncryptOutput>[]>;
180
+ /** Decrypt many values; throws the first item error. */
181
+ decryptMany(items: DecryptInput[], opts?: {
182
+ purpose?: string;
183
+ }): Promise<DecryptOutput[]>;
184
+ /**
185
+ * Decrypt many values, reporting per-item outcomes. Uses `/v1/decrypt/batch`
186
+ * when the credential holds `cipher.decrypt.batch`; otherwise falls back to
187
+ * individual requests (bounded concurrency).
188
+ */
189
+ decryptManySettled(items: DecryptInput[], opts?: {
190
+ purpose?: string;
191
+ }): Promise<Settled<DecryptOutput>[]>;
192
+ /** Rewrap ciphertexts to the key's current version without exposing plaintext (`cipher.rewrap`). */
193
+ rewrapMany(items: {
194
+ ciphertext: string;
195
+ context: CipherContext;
196
+ }[], opts?: {
197
+ jobId?: string;
198
+ }): Promise<Settled<{
199
+ ciphertext: string;
200
+ keyVersion: number;
201
+ rewrapped: boolean;
202
+ }>[]>;
203
+ /** Compute blind indexes for existing ciphertext (backfill / reindex). */
204
+ indexMany(items: {
205
+ ciphertext: string;
206
+ context: CipherContext;
207
+ }[], opts?: {
208
+ normalization?: Normalization;
209
+ }): Promise<Settled<string>[]>;
210
+ /**
211
+ * Exact-match search tokens. Returns one list per value: match a row when its
212
+ * stored blind index equals ANY token in the list (active + query-only index versions).
213
+ */
214
+ searchTokens(input: {
215
+ keyId?: string;
216
+ collection: string;
217
+ field: string;
218
+ values: (string | Uint8Array)[];
219
+ normalization?: Normalization;
220
+ }): Promise<string[][]>;
221
+ /**
222
+ * A Northstar Search index (encrypted full-text search inside the enclave).
223
+ * Cheap: no request is made until a method of the index is called.
224
+ *
225
+ * ```ts
226
+ * const customers = cipher.index('customers')
227
+ * await customers.ensure({ language: 'spanish', fields: { name: 'text', city: 'keyword' } })
228
+ * await customers.upsert({ 42: { name: 'Ana García', city: 'CDMX' } })
229
+ * const { hits } = await customers.search({ q: 'ana garsia', filter: { city: 'CDMX' } })
230
+ * ```
231
+ */
232
+ index(name: string): SearchIndex;
233
+ /** Search indexes this credential may query or write. */
234
+ searchIndexes(): Promise<SearchIndexInfo[]>;
235
+ /** Establish (or reuse) the attested session and describe it. */
236
+ session(): Promise<SessionDetails>;
237
+ }