Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/onepassword-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@vercel/sandbox": minor
"sandbox": minor
---

Add 1Password integration: inject secrets into sandbox env via integrations.onePassword.secrets (op:// refs), resolve op:// in CLI exec --env, add example and docs for service account token.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,35 @@ const sandbox = await Sandbox.create({
});
```

## 1Password secrets

Inject 1Password secrets into the sandbox using [secret references][op-secret-refs] (`op://vault/item/field`). Pass them in `integrations.onePassword.secrets` when creating a sandbox. They are resolved at creation and available to every command.

```ts
const sandbox = await Sandbox.create({
source: { url: "https://github.com/you/your-repo.git", type: "git" },
integrations: {
onePassword: {
secrets: {
SOME_PRIVATE_KEY: "op://My Vault/My Item/private key",
},
},
},
});
```
### Service account token

The integration needs a [1Password service account][op-service-account]. The SDK reads `OP_SERVICE_ACCOUNT_TOKEN` from the process environment when resolving `op://` references. You can set it in any of these ways:

For local development you can use the 1Password desktop app instead of a token: set `OP_ACCOUNT` to your 1Password account name and leave `OP_SERVICE_ACCOUNT_TOKEN` unset. The 1Password app must be running and unlocked, with the Developer setting enabled. See [1Password SDK authentication][op-sdk-auth].

**Vercel**
Add `OP_SERVICE_ACCOUNT_TOKEN` in your Vercel project or team under **Settings → Environment Variables**. Make sure to link the variable to your project so it syncs when you run `vercel env pull`.

**Local development**
1. Add `OP_SERVICE_ACCOUNT_TOKEN` to your `.env` or `.env.local` (or run `vercel env pull` to pull Vercel env vars into `.env.local`).
2. Run your script so it loads that file (e.g. `node --env-file=.env.local your-script.mjs` or use a loader like `dotenv`).

## Limitations

- Max resources: 8 vCPUs. You will get 2048 MB of memory per vCPU.
Expand Down Expand Up @@ -209,6 +238,9 @@ available [here](https://docs.aws.amazon.com/linux/al2023/release-notes/all-pack
[create-token]: https://vercel.com/account/settings/tokens
[hive]: https://vercel.com/blog/a-deep-dive-into-hive-vercels-builds-infrastructure
[al-2023-packages]: https://docs.aws.amazon.com/linux/al2023/release-notes/all-packages-AL2023.7.html
[op-secret-refs]: https://developer.1password.com/docs/cli/secret-references/
[op-service-account]: https://developer.1password.com/docs/service-accounts/
[op-sdk-auth]: https://developer.1password.com/docs/sdks/concepts/#authentication

## Authors

Expand Down
8 changes: 8 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ Demonstrates how to install system packages using the `dnf` package manager with
- Amazon Linux package management
- Development environment setup

### 🔑 [1Password Secrets Example](./onepassword-secrets)

Shows how to inject 1Password secrets into a sandbox using secret references. Features include:

- 1Password secret references (`op://vault/item/field`) in `integrations.onePassword.secrets`
- Service account or desktop app authentication
- Resolved secrets available to every command in the sandbox

### 📊 [Python Charts Example](./charts-python)

Shows how to use Vercel's AI Gateway with OpenAI GPT-4 to generate and execute Python chart code in a secure sandbox. Features include:
Expand Down
66 changes: 66 additions & 0 deletions examples/onepassword-secrets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 1Password Secrets Example

This example demonstrates how to inject 1Password secrets into a sandbox using secret references. It creates a sandbox with a secret resolved from 1Password, runs a command that uses it, and confirms the value is available in the environment.

## Features

- **Secret References**: Pass 1Password secret references (`op://vault/item/field`) in `integrations.onePassword.secrets` when creating a sandbox
- **Environment Injection**: Resolved secrets are merged into the environment for every command run in the sandbox
- **Optional Env Var**: Read the reference from `OP_REF` in `.env.local` so you don't hardcode it in code
- **Authentication**: Uses `OP_SERVICE_ACCOUNT_TOKEN` for 1Password

## Prerequisites

1. **Store a secret in 1Password**: In 1Password, create or use a vault item with a field that holds the secret you want in the sandbox. Note the secret reference (e.g. `op://Your Vault/Your Item/field name`). You’ll use this as `OP_REF` in step 4 below.

2. **Create a service account**: Use a [1Password service account](https://developer.1password.com/docs/service-accounts/) so the sandbox can read that secret. Create one in 1Password, grant it access to the vault that contains the item, and use its token for authentication.
1. Create a service account in 1Password.
2. Grant it access to the vault that contains your secret item.
3. Copy its token; you’ll add it as `OP_SERVICE_ACCOUNT_TOKEN` in the “How to Run” section below.

## How to Run

1. Navigate to the example directory:

```bash
cd examples/onepassword-secrets
```

2. Install dependencies:

```bash
pnpm install
```

3. Set up authentication for Vercel Sandbox:

```bash
vercel link
vercel env pull
```

4. Add these to `.env.local` (from [Prerequisites](#prerequisites): token from step 2, reference from step 1):

**Service account token** (so the sandbox can read 1Password):
```bash
OP_SERVICE_ACCOUNT_TOKEN="your-token"
```
**Secret reference** (the op:// path to your vault item field):
```bash
OP_REF="op://Your Vault/Your Item/field name"
```

6. Run the example

```bash
pnpm start
```

You should see output like:

- `Creating sandbox with 1Password secrets...`
- `Sandbox created. Running command that uses the secret...`
- `MY_SECRET is set: yes` and a non-zero length (e.g. `Length: 8`)
- `Done.`

If you see `MY_SECRET is set:` with nothing after it and `Length: 0`, check that `OP_REF` and `OP_SERVICE_ACCOUNT_TOKEN` are set correctly in `.env.local` and that the reference uses only one pair of quotes.
29 changes: 29 additions & 0 deletions examples/onepassword-secrets/onepassword-secrets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Sandbox } from "@vercel/sandbox";

async function main() {
console.log("Creating sandbox with 1Password secrets...\n");

const sandbox = await Sandbox.create({
timeout: 30000,
integrations: {
onePassword: {
secrets: {
MY_SECRET: process.env.OP_REF,
},
},
},
});

console.log("Sandbox created. Running command that uses the secret...\n");

const result = await sandbox.runCommand("bash", [
"-c",
"echo \"MY_SECRET is set: ${MY_SECRET:+yes}\" && echo \"Length: ${#MY_SECRET}\"",
]);

console.log(await result.stdout());
await sandbox.stop();
console.log("\nDone.");
}

main().catch(console.error);
17 changes: 17 additions & 0 deletions examples/onepassword-secrets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "onepassword-secrets-example",
"version": "0.1.0",
"private": true,
"description": "Example: inject 1Password secrets into a sandbox via secret references",
"main": "onepassword-secrets.ts",
"scripts": {
"start": "node --env-file .env.local --experimental-strip-types onepassword-secrets.ts"
},
"dependencies": {
"@vercel/sandbox": "workspace:*"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.0.0"
}
}
2 changes: 1 addition & 1 deletion packages/sandbox/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Flags:
Options:

--workdir, -w <str> The working directory to run the command in [optional]
--env <key=value>, -e=<key=value> Environment variables to set for the command
--env <key=value>, -e=<key=value> Environment variables for the command. 1Password refs (op://...) supported.

Auth & Scope:

Expand Down
12 changes: 9 additions & 3 deletions packages/sandbox/src/commands/exec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Sandbox } from "@vercel/sandbox";
import { Sandbox, resolveOpSecretsInEnv } from "@vercel/sandbox";
import * as cmd from "cmd-ts";
import { sandboxId } from "../args/sandbox-id";
import { isatty } from "node:tty";
Expand All @@ -7,6 +7,7 @@ import { printCommand } from "../util/print-command";
import { ObjectFromKeyValue } from "../args/key-value-pair";
import { scope } from "../args/scope";
import { sandboxClient } from "../client";
import { version } from "../pkg";
import chalk from "chalk";

export const args = {
Expand Down Expand Up @@ -110,6 +111,11 @@ export const exec = cmd.command({
return;
}

const resolvedEnv = await resolveOpSecretsInEnv(
envVars,
`v${version}`,
);

if (!interactive) {
console.error(printCommand(command, args));
const result = await sandbox.runCommand({
Expand All @@ -119,7 +125,7 @@ export const exec = cmd.command({
stdout: process.stdout,
sudo: asSudo,
cwd,
env: envVars,
env: resolvedEnv,
});

process.exitCode = result.exitCode;
Expand All @@ -128,7 +134,7 @@ export const exec = cmd.command({
sandbox,
cwd,
execution: [command, ...args],
envVars,
envVars: resolvedEnv,
sudo: asSudo,
skipExtendingTimeout,
});
Expand Down
32 changes: 32 additions & 0 deletions packages/vercel-sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,35 @@ const sandbox = await Sandbox.create({
});
```

## 1Password secrets

Inject 1Password secrets into the sandbox using [secret references][op-secret-refs] (`op://vault/item/field`). Pass them in `integrations.onePassword.secrets` when creating a sandbox. They are resolved at creation and available to every command.

```ts
const sandbox = await Sandbox.create({
source: { url: "https://github.com/you/your-repo.git", type: "git" },
integrations: {
onePassword: {
secrets: {
SOME_PRIVATE_KEY: "op://My Vault/My Item/private key",
},
},
},
});
```
### Service account token

The integration needs a [1Password service account][op-service-account]. The SDK reads `OP_SERVICE_ACCOUNT_TOKEN` from the process environment when resolving `op://` references. You can set it in any of these ways:

For local development you can use the 1Password desktop app instead of a token: set `OP_ACCOUNT` to your 1Password account name and leave `OP_SERVICE_ACCOUNT_TOKEN` unset. The 1Password app must be running and unlocked, with the Developer setting enabled. See [1Password SDK authentication][op-sdk-auth].

**Vercel**
Add `OP_SERVICE_ACCOUNT_TOKEN` in your Vercel project or team under **Settings → Environment Variables**.

**Local development**
1. Add `OP_SERVICE_ACCOUNT_TOKEN` to your `.env` or `.env.local` (or run `vercel env pull` to pull Vercel env vars into `.env.local`).
2. Run your script so it loads that file (e.g. `node --env-file=.env.local your-script.mjs` or use a loader like `dotenv`).

## Limitations

- Max resources: 8 vCPUs. You will get 2048 MB of memory per vCPU.
Expand Down Expand Up @@ -209,6 +238,9 @@ available [here](https://docs.aws.amazon.com/linux/al2023/release-notes/all-pack
[create-token]: https://vercel.com/account/settings/tokens
[hive]: https://vercel.com/blog/a-deep-dive-into-hive-vercels-builds-infrastructure
[al-2023-packages]: https://docs.aws.amazon.com/linux/al2023/release-notes/all-packages-AL2023.7.html
[op-secret-refs]: https://developer.1password.com/docs/cli/secret-references/
[op-service-account]: https://developer.1password.com/docs/service-accounts/
[op-sdk-auth]: https://developer.1password.com/docs/sdks/concepts/#authentication

## Authors

Expand Down
3 changes: 3 additions & 0 deletions packages/vercel-sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@
"typedoc": "0.28.5",
"typescript": "5.8.3",
"vitest": "catalog:"
},
"optionalDependencies": {
"@1password/sdk": "^0.4.0"
}
}
1 change: 1 addition & 0 deletions packages/vercel-sandbox/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { Snapshot } from "./snapshot";
export { Command, CommandFinished } from "./command";
export { StreamError } from "./api-client/api-error";
export { APIError } from "./api-client/api-error";
export { resolveOpSecretsInEnv } from "./utils/resolve-op-secrets";
Loading