@pgpm/encrypted-secrets 0.4.0 → 0.6.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.
package/Makefile
CHANGED
package/README.md
CHANGED
|
@@ -2,4 +2,563 @@
|
|
|
2
2
|
|
|
3
3
|
Encrypted secrets management for PostgreSQL.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@pgpm/encrypted-secrets` provides a comprehensive API layer for managing encrypted secrets in PostgreSQL. Built on top of `@pgpm/encrypted-secrets-table`, this package offers high-level functions for storing, retrieving, verifying, and deleting encrypted secrets with support for multiple encryption methods (PGP and crypt). It includes role-based access control and integrates seamlessly with PostgreSQL's encryption extensions.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Multiple Encryption Methods**: Support for PGP symmetric encryption and crypt hashing
|
|
12
|
+
- **High-Level API**: Simple functions for secret management (upsert, get, verify, delete)
|
|
13
|
+
- **Role-Based Access**: Grants execute permissions to authenticated users
|
|
14
|
+
- **Automatic Encryption**: Secrets are encrypted before storage
|
|
15
|
+
- **Verification Support**: Verify secrets without exposing stored values
|
|
16
|
+
- **Batch Operations**: Delete multiple secrets at once
|
|
17
|
+
- **Default Values**: Return default values when secrets don't exist
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
If you have `pgpm` installed:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pgpm install @pgpm/encrypted-secrets
|
|
25
|
+
pgpm deploy
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This is a quick way to get started. The sections below provide more detailed installation options.
|
|
29
|
+
|
|
30
|
+
### Prerequisites
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install pgpm globally
|
|
34
|
+
npm install -g pgpm
|
|
35
|
+
|
|
36
|
+
# Start PostgreSQL
|
|
37
|
+
pgpm docker start
|
|
38
|
+
|
|
39
|
+
# Set environment variables
|
|
40
|
+
eval "$(pgpm env)"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Deploy
|
|
44
|
+
|
|
45
|
+
#### Option 1: Deploy by installing with pgpm
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pgpm install @pgpm/encrypted-secrets
|
|
49
|
+
pgpm deploy
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### Option 2: Deploy from Package Directory
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cd packages/security/encrypted-secrets
|
|
56
|
+
pgpm deploy --createdb
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### Option 3: Deploy from Workspace Root
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install workspace dependencies
|
|
63
|
+
pnpm install
|
|
64
|
+
|
|
65
|
+
# Deploy with dependencies
|
|
66
|
+
pgpm deploy mydb1 --yes --createdb
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Core Functions
|
|
70
|
+
|
|
71
|
+
### encrypted_secrets.secrets_upsert()
|
|
72
|
+
|
|
73
|
+
Insert or update a secret.
|
|
74
|
+
|
|
75
|
+
**Signature:**
|
|
76
|
+
```sql
|
|
77
|
+
encrypted_secrets.secrets_upsert(
|
|
78
|
+
v_secrets_owned_field uuid,
|
|
79
|
+
secret_name text,
|
|
80
|
+
secret_value text,
|
|
81
|
+
field_encoding text DEFAULT 'pgp'
|
|
82
|
+
) RETURNS boolean
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Parameters:**
|
|
86
|
+
- `v_secrets_owned_field`: UUID of the entity that owns this secret
|
|
87
|
+
- `secret_name`: Name/identifier for the secret
|
|
88
|
+
- `secret_value`: The secret value to encrypt and store
|
|
89
|
+
- `field_encoding`: Encryption method ('pgp' or 'crypt')
|
|
90
|
+
|
|
91
|
+
**Returns:** `TRUE` on success
|
|
92
|
+
|
|
93
|
+
**Usage:**
|
|
94
|
+
```sql
|
|
95
|
+
-- Store a secret with PGP encryption
|
|
96
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
97
|
+
'user-uuid',
|
|
98
|
+
'github_token',
|
|
99
|
+
'ghp_xxxxxxxxxxxx',
|
|
100
|
+
'pgp'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
-- Store a password with crypt hashing
|
|
104
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
105
|
+
'user-uuid',
|
|
106
|
+
'password',
|
|
107
|
+
'my-secure-password',
|
|
108
|
+
'crypt'
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### encrypted_secrets.secrets_getter()
|
|
113
|
+
|
|
114
|
+
Retrieve and decrypt a secret.
|
|
115
|
+
|
|
116
|
+
**Signature:**
|
|
117
|
+
```sql
|
|
118
|
+
encrypted_secrets.secrets_getter(
|
|
119
|
+
secrets_owned_field uuid,
|
|
120
|
+
secret_name text,
|
|
121
|
+
default_value text DEFAULT NULL
|
|
122
|
+
) RETURNS text
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Parameters:**
|
|
126
|
+
- `secrets_owned_field`: UUID of the secret owner
|
|
127
|
+
- `secret_name`: Name of the secret to retrieve
|
|
128
|
+
- `default_value`: Value to return if secret doesn't exist
|
|
129
|
+
|
|
130
|
+
**Returns:** Decrypted secret value or default value
|
|
131
|
+
|
|
132
|
+
**Usage:**
|
|
133
|
+
```sql
|
|
134
|
+
-- Get a secret
|
|
135
|
+
SELECT encrypted_secrets.secrets_getter(
|
|
136
|
+
'user-uuid',
|
|
137
|
+
'github_token'
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
-- Get a secret with default value
|
|
141
|
+
SELECT encrypted_secrets.secrets_getter(
|
|
142
|
+
'user-uuid',
|
|
143
|
+
'api_key',
|
|
144
|
+
'default-key-value'
|
|
145
|
+
);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### encrypted_secrets.secrets_verify()
|
|
149
|
+
|
|
150
|
+
Verify a secret without retrieving it.
|
|
151
|
+
|
|
152
|
+
**Signature:**
|
|
153
|
+
```sql
|
|
154
|
+
encrypted_secrets.secrets_verify(
|
|
155
|
+
secrets_owned_field uuid,
|
|
156
|
+
secret_name text,
|
|
157
|
+
secret_value text
|
|
158
|
+
) RETURNS boolean
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Parameters:**
|
|
162
|
+
- `secrets_owned_field`: UUID of the secret owner
|
|
163
|
+
- `secret_name`: Name of the secret to verify
|
|
164
|
+
- `secret_value`: Value to verify against stored secret
|
|
165
|
+
|
|
166
|
+
**Returns:** `TRUE` if values match, `FALSE` otherwise
|
|
167
|
+
|
|
168
|
+
**Usage:**
|
|
169
|
+
```sql
|
|
170
|
+
-- Verify a password
|
|
171
|
+
SELECT encrypted_secrets.secrets_verify(
|
|
172
|
+
'user-uuid',
|
|
173
|
+
'password',
|
|
174
|
+
'user-provided-password'
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
-- Use in authentication
|
|
178
|
+
DO $$
|
|
179
|
+
DECLARE
|
|
180
|
+
is_valid boolean;
|
|
181
|
+
BEGIN
|
|
182
|
+
SELECT encrypted_secrets.secrets_verify(
|
|
183
|
+
current_user_id(),
|
|
184
|
+
'password',
|
|
185
|
+
'user-input'
|
|
186
|
+
) INTO is_valid;
|
|
187
|
+
|
|
188
|
+
IF NOT is_valid THEN
|
|
189
|
+
RAISE EXCEPTION 'Invalid password';
|
|
190
|
+
END IF;
|
|
191
|
+
END $$;
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### encrypted_secrets.secrets_delete()
|
|
195
|
+
|
|
196
|
+
Delete one or more secrets.
|
|
197
|
+
|
|
198
|
+
**Signatures:**
|
|
199
|
+
```sql
|
|
200
|
+
-- Delete single secret
|
|
201
|
+
encrypted_secrets.secrets_delete(
|
|
202
|
+
secrets_owned_field uuid,
|
|
203
|
+
secret_name text
|
|
204
|
+
) RETURNS void
|
|
205
|
+
|
|
206
|
+
-- Delete multiple secrets
|
|
207
|
+
encrypted_secrets.secrets_delete(
|
|
208
|
+
secrets_owned_field uuid,
|
|
209
|
+
secret_names text[]
|
|
210
|
+
) RETURNS void
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Usage:**
|
|
214
|
+
```sql
|
|
215
|
+
-- Delete a single secret
|
|
216
|
+
SELECT encrypted_secrets.secrets_delete(
|
|
217
|
+
'user-uuid',
|
|
218
|
+
'old_api_key'
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
-- Delete multiple secrets
|
|
222
|
+
SELECT encrypted_secrets.secrets_delete(
|
|
223
|
+
'user-uuid',
|
|
224
|
+
ARRAY['temp_token', 'expired_key', 'old_password']
|
|
225
|
+
);
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Encryption Methods
|
|
229
|
+
|
|
230
|
+
### PGP Encryption
|
|
231
|
+
|
|
232
|
+
PGP (Pretty Good Privacy) symmetric encryption provides strong encryption for secrets:
|
|
233
|
+
|
|
234
|
+
```sql
|
|
235
|
+
-- Store with PGP
|
|
236
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
237
|
+
'owner-uuid',
|
|
238
|
+
'api_key',
|
|
239
|
+
'secret-value',
|
|
240
|
+
'pgp'
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
-- Retrieval automatically decrypts
|
|
244
|
+
SELECT encrypted_secrets.secrets_getter('owner-uuid', 'api_key');
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Characteristics:**
|
|
248
|
+
- Reversible encryption (can decrypt)
|
|
249
|
+
- Uses owner UUID as encryption key
|
|
250
|
+
- Suitable for API keys, tokens, connection strings
|
|
251
|
+
|
|
252
|
+
### Crypt Hashing
|
|
253
|
+
|
|
254
|
+
Crypt provides one-way hashing for passwords:
|
|
255
|
+
|
|
256
|
+
```sql
|
|
257
|
+
-- Store with crypt
|
|
258
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
259
|
+
'user-uuid',
|
|
260
|
+
'password',
|
|
261
|
+
'user-password',
|
|
262
|
+
'crypt'
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
-- Verify password
|
|
266
|
+
SELECT encrypted_secrets.secrets_verify(
|
|
267
|
+
'user-uuid',
|
|
268
|
+
'password',
|
|
269
|
+
'user-provided-password'
|
|
270
|
+
);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Characteristics:**
|
|
274
|
+
- One-way hashing (cannot decrypt)
|
|
275
|
+
- Suitable for passwords
|
|
276
|
+
- Use `secrets_verify()` to check passwords
|
|
277
|
+
|
|
278
|
+
## Usage Patterns
|
|
279
|
+
|
|
280
|
+
### User Authentication
|
|
281
|
+
|
|
282
|
+
```sql
|
|
283
|
+
-- Register user with password
|
|
284
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
285
|
+
user_id,
|
|
286
|
+
'password',
|
|
287
|
+
'user-chosen-password',
|
|
288
|
+
'crypt'
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
-- Login verification
|
|
292
|
+
CREATE FUNCTION authenticate_user(
|
|
293
|
+
p_user_id uuid,
|
|
294
|
+
p_password text
|
|
295
|
+
) RETURNS boolean AS $$
|
|
296
|
+
BEGIN
|
|
297
|
+
RETURN encrypted_secrets.secrets_verify(
|
|
298
|
+
p_user_id,
|
|
299
|
+
'password',
|
|
300
|
+
p_password
|
|
301
|
+
);
|
|
302
|
+
END;
|
|
303
|
+
$$ LANGUAGE plpgsql;
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### API Key Management
|
|
307
|
+
|
|
308
|
+
```sql
|
|
309
|
+
-- Store API keys for a service
|
|
310
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
311
|
+
organization_id,
|
|
312
|
+
'stripe_secret_key',
|
|
313
|
+
'sk_live_xxxxxxxxxxxx',
|
|
314
|
+
'pgp'
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
318
|
+
organization_id,
|
|
319
|
+
'stripe_publishable_key',
|
|
320
|
+
'pk_live_xxxxxxxxxxxx',
|
|
321
|
+
'pgp'
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
-- Retrieve for use
|
|
325
|
+
CREATE FUNCTION get_stripe_config(org_id uuid)
|
|
326
|
+
RETURNS jsonb AS $$
|
|
327
|
+
BEGIN
|
|
328
|
+
RETURN jsonb_build_object(
|
|
329
|
+
'secret_key', encrypted_secrets.secrets_getter(org_id, 'stripe_secret_key'),
|
|
330
|
+
'publishable_key', encrypted_secrets.secrets_getter(org_id, 'stripe_publishable_key')
|
|
331
|
+
);
|
|
332
|
+
END;
|
|
333
|
+
$$ LANGUAGE plpgsql;
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Database Credentials
|
|
337
|
+
|
|
338
|
+
```sql
|
|
339
|
+
-- Store database connection strings
|
|
340
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
341
|
+
app_id,
|
|
342
|
+
'db_connection_string',
|
|
343
|
+
'postgresql://user:pass@host:5432/db',
|
|
344
|
+
'pgp'
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
-- Retrieve for connection
|
|
348
|
+
SELECT encrypted_secrets.secrets_getter(
|
|
349
|
+
app_id,
|
|
350
|
+
'db_connection_string'
|
|
351
|
+
);
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Secret Rotation
|
|
355
|
+
|
|
356
|
+
```sql
|
|
357
|
+
-- Rotate an API key
|
|
358
|
+
DO $$
|
|
359
|
+
DECLARE
|
|
360
|
+
old_key text;
|
|
361
|
+
BEGIN
|
|
362
|
+
-- Get old key for migration
|
|
363
|
+
old_key := encrypted_secrets.secrets_getter('org-uuid', 'api_key');
|
|
364
|
+
|
|
365
|
+
-- Store new key
|
|
366
|
+
PERFORM encrypted_secrets.secrets_upsert(
|
|
367
|
+
'org-uuid',
|
|
368
|
+
'api_key',
|
|
369
|
+
'new-api-key-value',
|
|
370
|
+
'pgp'
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
-- Optionally keep old key temporarily
|
|
374
|
+
PERFORM encrypted_secrets.secrets_upsert(
|
|
375
|
+
'org-uuid',
|
|
376
|
+
'api_key_old',
|
|
377
|
+
old_key,
|
|
378
|
+
'pgp'
|
|
379
|
+
);
|
|
380
|
+
END $$;
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Security Considerations
|
|
384
|
+
|
|
385
|
+
1. **Encryption Keys**: The PGP encryption uses the owner UUID as the encryption key. Ensure UUIDs are not exposed.
|
|
386
|
+
|
|
387
|
+
2. **Access Control**: Functions are granted to `authenticated` role. Use RLS on the underlying table for additional security.
|
|
388
|
+
|
|
389
|
+
3. **Audit Logging**: Consider logging secret access:
|
|
390
|
+
```sql
|
|
391
|
+
CREATE TABLE secret_access_log (
|
|
392
|
+
id serial PRIMARY KEY,
|
|
393
|
+
owner_id uuid,
|
|
394
|
+
secret_name text,
|
|
395
|
+
accessed_at timestamptz DEFAULT now(),
|
|
396
|
+
accessed_by uuid
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
-- Wrap getter with logging
|
|
400
|
+
CREATE FUNCTION get_secret_with_audit(
|
|
401
|
+
owner_id uuid,
|
|
402
|
+
secret_name text
|
|
403
|
+
) RETURNS text AS $$
|
|
404
|
+
DECLARE
|
|
405
|
+
secret_value text;
|
|
406
|
+
BEGIN
|
|
407
|
+
secret_value := encrypted_secrets.secrets_getter(owner_id, secret_name);
|
|
408
|
+
|
|
409
|
+
INSERT INTO secret_access_log (owner_id, secret_name, accessed_by)
|
|
410
|
+
VALUES (owner_id, secret_name, current_user_id());
|
|
411
|
+
|
|
412
|
+
RETURN secret_value;
|
|
413
|
+
END;
|
|
414
|
+
$$ LANGUAGE plpgsql;
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
4. **Never Log Secrets**: Ensure application logs don't capture decrypted values.
|
|
418
|
+
|
|
419
|
+
5. **Use HTTPS**: Always transmit secrets over encrypted connections.
|
|
420
|
+
|
|
421
|
+
## Integration with Other Packages
|
|
422
|
+
|
|
423
|
+
### With @pgpm/encrypted-secrets-table
|
|
424
|
+
|
|
425
|
+
This package builds directly on the storage layer:
|
|
426
|
+
|
|
427
|
+
```sql
|
|
428
|
+
-- High-level API (this package)
|
|
429
|
+
SELECT encrypted_secrets.secrets_upsert('uuid', 'key', 'value');
|
|
430
|
+
|
|
431
|
+
-- Low-level storage (encrypted-secrets-table)
|
|
432
|
+
SELECT * FROM secrets_schema.secrets_table WHERE secrets_owned_field = 'uuid';
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### With @pgpm/default-roles
|
|
436
|
+
|
|
437
|
+
Combine with role-based access control:
|
|
438
|
+
|
|
439
|
+
```sql
|
|
440
|
+
-- Only authenticated users can manage secrets
|
|
441
|
+
GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_upsert TO authenticated;
|
|
442
|
+
GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_getter TO authenticated;
|
|
443
|
+
GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_verify TO authenticated;
|
|
444
|
+
GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_delete TO authenticated;
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### With @pgpm/jwt-claims
|
|
448
|
+
|
|
449
|
+
Use JWT claims for owner context:
|
|
450
|
+
|
|
451
|
+
```sql
|
|
452
|
+
-- Store secret for current user
|
|
453
|
+
SELECT encrypted_secrets.secrets_upsert(
|
|
454
|
+
jwt_public.current_user_id(),
|
|
455
|
+
'personal_api_key',
|
|
456
|
+
'key-value',
|
|
457
|
+
'pgp'
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
-- Get secret for current user
|
|
461
|
+
SELECT encrypted_secrets.secrets_getter(
|
|
462
|
+
jwt_public.current_user_id(),
|
|
463
|
+
'personal_api_key'
|
|
464
|
+
);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Dependencies
|
|
468
|
+
|
|
469
|
+
- `@pgpm/default-roles`: Role-based access control
|
|
470
|
+
- `@pgpm/encrypted-secrets-table`: Storage layer
|
|
471
|
+
- `@pgpm/verify`: Verification utilities
|
|
472
|
+
- PostgreSQL pgcrypto extension (for encryption functions)
|
|
473
|
+
|
|
474
|
+
## Testing
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
pnpm test
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Development
|
|
481
|
+
|
|
482
|
+
See the [Development](#development) section below for information on working with this package.
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## Development
|
|
487
|
+
|
|
488
|
+
### **Before You Begin**
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# 1. Install pgpm
|
|
492
|
+
npm install -g pgpm
|
|
493
|
+
|
|
494
|
+
# 2. Start Postgres (Docker or local)
|
|
495
|
+
pgpm docker start
|
|
496
|
+
|
|
497
|
+
# 3. Load PG* environment variables (PGHOST, PGUSER, ...)
|
|
498
|
+
eval "$(pgpm env)"
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
### **Starting a New Project**
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
# 1. Create a workspace
|
|
507
|
+
pgpm init --workspace
|
|
508
|
+
cd my-app
|
|
509
|
+
|
|
510
|
+
# 2. Create your first module
|
|
511
|
+
pgpm init
|
|
512
|
+
|
|
513
|
+
# 3. Add a migration
|
|
514
|
+
pgpm add some_change
|
|
515
|
+
|
|
516
|
+
# 4. Deploy (auto-creates database)
|
|
517
|
+
pgpm deploy --createdb
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
### **Working With an Existing Project**
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
# 1. Clone and enter the project
|
|
526
|
+
git clone <repo> && cd <project>
|
|
527
|
+
|
|
528
|
+
# 2. Install dependencies
|
|
529
|
+
pnpm install
|
|
530
|
+
|
|
531
|
+
# 3. Deploy locally
|
|
532
|
+
pgpm deploy --createdb
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
### **Testing a Module Inside a Workspace**
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# 1. Install workspace deps
|
|
541
|
+
pnpm install
|
|
542
|
+
|
|
543
|
+
# 2. Enter the module directory
|
|
544
|
+
cd packages/<some-module>
|
|
545
|
+
|
|
546
|
+
# 3. Run tests in watch mode
|
|
547
|
+
pnpm test:watch
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Related Tooling
|
|
551
|
+
|
|
552
|
+
* [pgpm](https://github.com/launchql/launchql/tree/main/packages/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
|
|
553
|
+
* [pgsql-test](https://github.com/launchql/launchql/tree/main/packages/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
|
|
554
|
+
* [supabase-test](https://github.com/launchql/launchql/tree/main/packages/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
|
|
555
|
+
* [graphile-test](https://github.com/launchql/launchql/tree/main/packages/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
|
|
556
|
+
* [pgsql-parser](https://github.com/launchql/pgsql-parser): **🔄 SQL conversion engine** that interprets and converts PostgreSQL syntax.
|
|
557
|
+
* [libpg-query-node](https://github.com/launchql/libpg-query-node): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
|
|
558
|
+
* [pg-proto-parser](https://github.com/launchql/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
|
|
559
|
+
|
|
560
|
+
## Disclaimer
|
|
561
|
+
|
|
562
|
+
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
|
|
563
|
+
|
|
564
|
+
No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# launchql-encrypted-secrets extension
|
|
2
2
|
comment = 'launchql-encrypted-secrets extension'
|
|
3
|
-
default_version = '0.
|
|
3
|
+
default_version = '0.5.0'
|
|
4
4
|
module_pathname = '$libdir/launchql-encrypted-secrets'
|
|
5
5
|
requires = 'pgcrypto,plpgsql,uuid-ossp,launchql-encrypted-secrets-table,launchql-default-roles,launchql-verify'
|
|
6
6
|
relocatable = false
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpm/encrypted-secrets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Encrypted secrets management for PostgreSQL",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"bundle": "
|
|
9
|
+
"bundle": "pgpm package",
|
|
10
10
|
"test": "jest",
|
|
11
11
|
"test:watch": "jest --watch"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@pgpm/default-roles": "0.
|
|
15
|
-
"@pgpm/encrypted-secrets-table": "0.
|
|
16
|
-
"@pgpm/verify": "0.
|
|
14
|
+
"@pgpm/default-roles": "0.6.0",
|
|
15
|
+
"@pgpm/encrypted-secrets-table": "0.6.0",
|
|
16
|
+
"@pgpm/verify": "0.6.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"
|
|
19
|
+
"pgpm": "^0.2.0"
|
|
20
20
|
},
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/launchql/
|
|
23
|
+
"url": "https://github.com/launchql/pgpm-modules"
|
|
24
24
|
},
|
|
25
|
-
"homepage": "https://github.com/launchql/
|
|
25
|
+
"homepage": "https://github.com/launchql/pgpm-modules",
|
|
26
26
|
"bugs": {
|
|
27
|
-
"url": "https://github.com/launchql/
|
|
27
|
+
"url": "https://github.com/launchql/pgpm-modules/issues"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "c7d0eae588d7a764b382a330c8b853b341b13fb2"
|
|
30
30
|
}
|
|
File without changes
|
/package/sql/{launchql-encrypted-secrets--0.1.0.sql → launchql-encrypted-secrets--0.5.0.sql}
RENAMED
|
File without changes
|