@cipherstash/stack 0.11.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @cipherstash/stack
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 15764a8: Implement stack auth into stash cli flow.
8
+
3
9
  ## 0.11.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -44,19 +44,29 @@ pnpm add @cipherstash/stack
44
44
 
45
45
  ## Quick Start
46
46
 
47
+ ### 1. Initialize and authenticate your project
48
+
49
+ ```bash
50
+ npx stash init
51
+ ```
52
+
53
+ The wizard will authenticate you, walk you through choosing a database connection method, build an encryption schema, and install the required dependencies.
54
+
55
+ ### 2. Encrypt and decrypt
56
+
47
57
  ```typescript
48
58
  import { Encryption } from "@cipherstash/stack"
49
59
  import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
50
60
 
51
- // 1. Define a schema
61
+ // Define a schema
52
62
  const users = encryptedTable("users", {
53
63
  email: encryptedColumn("email").equality().freeTextSearch(),
54
64
  })
55
65
 
56
- // 2. Create a client (reads CS_* env vars automatically)
66
+ // Create a client
57
67
  const client = await Encryption({ schemas: [users] })
58
68
 
59
- // 3. Encrypt a value
69
+ // Encrypt a value
60
70
  const encrypted = await client.encrypt("hello@example.com", {
61
71
  column: users.email,
62
72
  table: users,
@@ -68,7 +78,7 @@ if (encrypted.failure) {
68
78
  console.log("Encrypted payload:", encrypted.data)
69
79
  }
70
80
 
71
- // 4. Decrypt the value
81
+ // Decrypt the value
72
82
  const decrypted = await client.decrypt(encrypted.data)
73
83
 
74
84
  if (decrypted.failure) {
@@ -430,6 +440,16 @@ await secrets.delete("DATABASE_URL")
430
440
 
431
441
  The `stash` CLI is bundled with the package and available after install.
432
442
 
443
+ ### `stash auth`
444
+
445
+ Authenticate with CipherStash.
446
+
447
+ ```bash
448
+ npx stash auth login
449
+ ```
450
+
451
+ This runs the device code flow: it opens your browser, you confirm the code, and a token is saved to `~/.cipherstash/auth.json`. No environment variables or credentials files are needed for local development.
452
+
433
453
  ### `stash init`
434
454
 
435
455
  Initialize CipherStash for your project with an interactive wizard.
@@ -440,11 +460,13 @@ npx stash init --supabase
440
460
  ```
441
461
 
442
462
  The wizard will:
443
- 1. Choose your database connection method (Drizzle ORM, Supabase JS, Prisma, or Raw SQL)
444
- 2. Build an encryption schema interactively or use a placeholder, then generate the encryption client file
445
- 3. Install `@cipherstash/stack-forge` as a dev dependency for database tooling
463
+ 1. Authenticate with CipherStash (device code flow)
464
+ 2. Bind your device to the default Keyset
465
+ 3. Choose your database connection method (Drizzle ORM, Supabase JS, Prisma, or Raw SQL)
466
+ 4. Build an encryption schema interactively or use a placeholder, then generate the encryption client file
467
+ 5. Install `@cipherstash/stack-forge` as a dev dependency for database tooling
446
468
 
447
- After `stash init`, create a CipherStash account at [dashboard.cipherstash.com/sign-up](https://dashboard.cipherstash.com/sign-up) to get your credentials, then run `npx stash-forge setup` to configure your database connection.
469
+ After `stash init`, run `npx stash-forge setup` to configure your database.
448
470
 
449
471
  | Flag | Description |
450
472
  |------|-------------|
@@ -468,11 +490,15 @@ npx stash secrets delete -name DATABASE_URL -environment production
468
490
  | `stash secrets list` | `-environment` | `-e` | List all secret names in an environment |
469
491
  | `stash secrets delete` | `-name`, `-environment`, `-yes` | `-n`, `-e`, `-y` | Delete a secret (prompts for confirmation unless `-yes`) |
470
492
 
471
- The CLI reads credentials from the same `CS_*` environment variables described in [Configuration](#configuration).
472
-
473
493
  ## Configuration
474
494
 
475
- ### Environment Variables
495
+ ### Local Development
496
+
497
+ No environment variables or credentials are needed for local development. Run `npx @cipherstash/stack auth login` to authenticate via the device code flow, and the SDK and CLI will use the token saved to `~/.cipherstash/auth.json`.
498
+
499
+ ### Going to Production
500
+
501
+ For production, CI/CD, and deployed environments, you'll need to set up machine credentials via environment variables:
476
502
 
477
503
  | Variable | Description |
478
504
  |-----|-------|
@@ -481,13 +507,7 @@ The CLI reads credentials from the same `CS_*` environment variables described i
481
507
  | `CS_CLIENT_KEY` | Client key material used with ZeroKMS for encryption |
482
508
  | `CS_CLIENT_ACCESS_KEY` | API key for authenticating with the CipherStash API |
483
509
 
484
- Store these in a `.env` file or set them in your hosting platform.
485
-
486
- Sign up at [cipherstash.com/signup](https://cipherstash.com/signup) and follow the onboarding to generate credentials.
487
-
488
- ### TOML Config
489
-
490
- You can also configure credentials via `cipherstash.toml` and `cipherstash.secret.toml` files in your project root. See the [CipherStash docs](https://cipherstash.com/docs) for format details.
510
+ See the [Going to Production](https://cipherstash.com/docs/stack/going-to-production) guide for full details on creating machine clients, setting up access keys, and configuring CI/CD pipelines.
491
511
 
492
512
  ### Programmatic Config
493
513
 
package/dist/bin/stash.js CHANGED
@@ -1200,6 +1200,91 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
1200
1200
  } };
1201
1201
  };
1202
1202
 
1203
+ // src/bin/commands/auth/login.ts
1204
+ import auth from "@cipherstash/auth";
1205
+ var { beginDeviceCodeFlow, bindClientDevice } = auth;
1206
+ var regions = [
1207
+ { value: "ap-southeast-2.aws", label: "Asia Pacific (Sydney)" },
1208
+ { value: "eu-central-1.aws", label: "Europe (Frankfurt)" },
1209
+ { value: "eu-west-1.aws", label: "Europe (Ireland)" },
1210
+ { value: "us-east-1.aws", label: "US East (N. Virginia)" },
1211
+ { value: "us-east-2.aws", label: "US East (Ohio)" },
1212
+ { value: "us-west-1.aws", label: "US West (N. California)" },
1213
+ { value: "us-west-2.aws", label: "US West (Oregon)" }
1214
+ ];
1215
+ async function selectRegion() {
1216
+ const region = await ve({
1217
+ message: "Select a region",
1218
+ options: regions
1219
+ });
1220
+ if (pD(region)) {
1221
+ xe("Cancelled.");
1222
+ process.exit(0);
1223
+ }
1224
+ return region;
1225
+ }
1226
+ async function login(region) {
1227
+ const s = Y2();
1228
+ const pending = await beginDeviceCodeFlow(region, "cli");
1229
+ M2.info(`Your code is: ${pending.userCode}`);
1230
+ M2.info(`Visit: ${pending.verificationUriComplete}`);
1231
+ M2.info(`Code expires in: ${pending.expiresIn}s`);
1232
+ const opened = pending.openInBrowser();
1233
+ if (!opened) {
1234
+ M2.warn("Could not open browser \u2014 please visit the URL above manually.");
1235
+ }
1236
+ s.start("Waiting for authorization...");
1237
+ const auth2 = await pending.pollForToken();
1238
+ s.stop("Authenticated! Token saved to ~/.cipherstash/auth.json");
1239
+ M2.info(
1240
+ `Token expires at: ${new Date(auth2.expiresAt * 1e3).toISOString()}`
1241
+ );
1242
+ }
1243
+ async function bindDevice() {
1244
+ const s = Y2();
1245
+ s.start("Binding device to the default Keyset...");
1246
+ try {
1247
+ await bindClientDevice();
1248
+ s.stop("Your device has been bound to the default Keyset!");
1249
+ } catch (error) {
1250
+ s.stop("Failed to bind your device to the default Keyset!");
1251
+ M2.error(error instanceof Error ? error.message : "Unknown error");
1252
+ process.exit(1);
1253
+ }
1254
+ }
1255
+
1256
+ // src/bin/commands/auth/index.ts
1257
+ var HELP = `
1258
+ Usage: stash auth <command>
1259
+
1260
+ Commands:
1261
+ login Authenticate with CipherStash
1262
+
1263
+ Examples:
1264
+ stash auth login
1265
+ `.trim();
1266
+ async function authCommand(args) {
1267
+ const subcommand = args[0];
1268
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
1269
+ console.log(HELP);
1270
+ return;
1271
+ }
1272
+ switch (subcommand) {
1273
+ case "login":
1274
+ {
1275
+ const region = await selectRegion();
1276
+ await login(region);
1277
+ await bindDevice();
1278
+ }
1279
+ break;
1280
+ default:
1281
+ console.error(`Unknown auth command: ${subcommand}
1282
+ `);
1283
+ console.log(HELP);
1284
+ process.exit(1);
1285
+ }
1286
+ }
1287
+
1203
1288
  // src/bin/commands/init/providers/base.ts
1204
1289
  function createBaseProvider() {
1205
1290
  return {
@@ -1212,10 +1297,7 @@ function createBaseProvider() {
1212
1297
  { value: "raw-sql", label: "Raw SQL / pg" }
1213
1298
  ],
1214
1299
  getNextSteps(state) {
1215
- const steps = [
1216
- "Create a CipherStash account and get your credentials:\n https://dashboard.cipherstash.com/sign-up\n Then set: CS_WORKSPACE_CRN, CS_CLIENT_ID, CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY",
1217
- "Set up your database: npx stash-forge setup"
1218
- ];
1300
+ const steps = ["Set up your database: npx stash-forge setup"];
1219
1301
  if (state.clientFilePath) {
1220
1302
  steps.push(`Edit your encryption schema: ${state.clientFilePath}`);
1221
1303
  }
@@ -1241,10 +1323,7 @@ function createSupabaseProvider() {
1241
1323
  { value: "raw-sql", label: "Raw SQL / pg" }
1242
1324
  ],
1243
1325
  getNextSteps(state) {
1244
- const steps = [
1245
- "Create a CipherStash account and get your credentials:\n https://dashboard.cipherstash.com/sign-up\n Then set: CS_WORKSPACE_CRN, CS_CLIENT_ID, CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY",
1246
- "Set up your database: npx stash-forge setup"
1247
- ];
1326
+ const steps = ["Set up your database: npx stash-forge setup"];
1248
1327
  if (state.clientFilePath) {
1249
1328
  steps.push(`Edit your encryption schema: ${state.clientFilePath}`);
1250
1329
  }
@@ -1257,6 +1336,18 @@ function createSupabaseProvider() {
1257
1336
  };
1258
1337
  }
1259
1338
 
1339
+ // src/bin/commands/init/steps/authenticate.ts
1340
+ var authenticateStep = {
1341
+ id: "authenticate",
1342
+ name: "Authenticate with CipherStash",
1343
+ async run(state, _provider) {
1344
+ const region = await selectRegion();
1345
+ await login(region);
1346
+ await bindDevice();
1347
+ return { ...state, authenticated: true };
1348
+ }
1349
+ };
1350
+
1260
1351
  // src/bin/commands/init/steps/build-schema.ts
1261
1352
  import { existsSync as existsSync2, mkdirSync, writeFileSync } from "node:fs";
1262
1353
  import { dirname, resolve as resolve2 } from "node:path";
@@ -1658,6 +1749,7 @@ var PROVIDER_MAP = {
1658
1749
  supabase: createSupabaseProvider
1659
1750
  };
1660
1751
  var STEPS = [
1752
+ authenticateStep,
1661
1753
  selectConnectionStep,
1662
1754
  buildSchemaStep,
1663
1755
  installForgeStep,
@@ -5050,7 +5142,7 @@ function requireFlag(flags, name) {
5050
5142
  }
5051
5143
  return val;
5052
5144
  }
5053
- var HELP = `
5145
+ var HELP2 = `
5054
5146
  ${style.title("Usage:")} stash secrets <command> [options]
5055
5147
 
5056
5148
  ${style.title("Commands:")}
@@ -5077,7 +5169,7 @@ async function secretsCommand(args) {
5077
5169
  const subcommand = args[0];
5078
5170
  const rest = args.slice(1);
5079
5171
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
5080
- console.log(HELP);
5172
+ console.log(HELP2);
5081
5173
  return;
5082
5174
  }
5083
5175
  const flags = parseFlags(rest);
@@ -5127,7 +5219,7 @@ async function secretsCommand(args) {
5127
5219
  }
5128
5220
  default:
5129
5221
  console.error(style.error(`Unknown secrets command: ${subcommand}`));
5130
- console.log(HELP);
5222
+ console.log(HELP2);
5131
5223
  process.exit(1);
5132
5224
  }
5133
5225
  }
@@ -5138,13 +5230,14 @@ var __dirname = dirname2(fileURLToPath(import.meta.url));
5138
5230
  var pkg = JSON.parse(
5139
5231
  readFileSync(join(__dirname, "../../package.json"), "utf-8")
5140
5232
  );
5141
- var HELP2 = `
5233
+ var HELP3 = `
5142
5234
  CipherStash Stack CLI v${pkg.version}
5143
5235
 
5144
5236
  Usage: stash <command> [options]
5145
5237
 
5146
5238
  Commands:
5147
5239
  init Initialize CipherStash for your project
5240
+ auth Authenticate with CipherStash
5148
5241
  secrets Manage encrypted secrets
5149
5242
 
5150
5243
  Options:
@@ -5157,6 +5250,7 @@ Init Flags:
5157
5250
  Examples:
5158
5251
  stash init
5159
5252
  stash init --supabase
5253
+ stash auth login
5160
5254
  stash secrets set -n DATABASE_URL -V "postgres://..." -e production
5161
5255
  stash secrets get -n DATABASE_URL -e production
5162
5256
  stash secrets get-many -n DATABASE_URL,API_KEY -e production
@@ -5181,7 +5275,7 @@ function parseArgs(argv) {
5181
5275
  async function main() {
5182
5276
  const { command, rest, booleanFlags } = parseArgs(process.argv);
5183
5277
  if (!command || command === "--help" || command === "-h") {
5184
- console.log(HELP2);
5278
+ console.log(HELP3);
5185
5279
  return;
5186
5280
  }
5187
5281
  if (command === "--version" || command === "-v") {
@@ -5192,13 +5286,16 @@ async function main() {
5192
5286
  case "init":
5193
5287
  await initCommand(booleanFlags);
5194
5288
  break;
5289
+ case "auth":
5290
+ await authCommand(rest);
5291
+ break;
5195
5292
  case "secrets":
5196
5293
  await secretsCommand(rest);
5197
5294
  break;
5198
5295
  default:
5199
5296
  console.error(`Unknown command: ${command}
5200
5297
  `);
5201
- console.log(HELP2);
5298
+ console.log(HELP3);
5202
5299
  process.exit(1);
5203
5300
  }
5204
5301
  }