---
title: 1Password Plugin
description: Using 1Password with Varlock
---

import { Steps, Icon } from '@astrojs/starlight/components';
import Badge from '@/components/Badge.astro';

<div class="page-badges">
  <Badge npmPackage="@varlock/1password-plugin" />
</div>

Our [1Password](https://1password.com/) plugin enables secure loading of values from 1Password vaults using declarative instructions within your `.env` files.

For local development, it (optionally) supports authenticating using the local 1Password desktop app, including using biometric unlock. Otherwise, it uses a [service account](https://www.1password.dev/service-accounts/) making it suitable for CI/CD and production environments.

This plugin is compatible with any 1Password account type (personal, family, teams, business), but note that [rate limits](https://www.1password.dev/service-accounts/rate-limits/) vary by account type.

## Installation and setup

In a JS/TS project, you may install the `@varlock/1password-plugin` package as a normal dependency.
Otherwise you can just load it directly from your `.env.schema` file, as long as you add a version specifier.
See the [plugins guide](/guides/plugins/#installation) for more instructions on installing plugins.

```env-spec title=".env.schema"
# 1. Load the plugin
# @plugin(@varlock/1password-plugin)
#
# 2. Initialize the plugin - see below for more details on options
# @initOp(token=$OP_TOKEN, allowAppAuth=forEnv(dev), account=acmeco)
# ---

# 3. Add a service account token config item (if applicable)
# @type=opServiceAccountToken @sensitive @internal
OP_TOKEN=
```

### Vault setup

If your secrets are already stored in 1Password, you may not need to do anything. However, if secrets live in a vault that holds other sensitive data, you should create a new vault and move your secrets to it, because **the access system of 1Password is based on vaults, not individual items**.

You can create multiple vaults to segment access to different environments, services, etc. This can be done using any 1Password app, the web app, or the CLI. [link](https://support.1password.com/create-share-vaults/#create-a-vault)

Remember to grant access to necessary team members, particularly if you plan on using the desktop app auth method during local development, as they will be authenticating as themselves.

:::tip[Vault organization best practices]
Consider how you want to organize your vaults and service accounts, keeping in mind [best practices](https://support.1password.com/business-security-practices/#access-management-and-the-principle-of-least-privilege). At a minimum, we recommend having a vault for highly sensitive production secrets and another for everything else.
:::

### Service account setup (for deployed environments)

If you plan on using data from 1Password in deployed environments (CI/CD, production, etc), you will need to create a [service account](https://www.1password.dev/service-accounts/get-started/) to allow machine-to-machine authentication. You could also use a service account for local development, although we recommend using the desktop app auth method described below for convenience.

This service account token will now serve as your _secret-zero_, granting access to the rest of your sensitive data stored in 1Password. For local-only workflows, you can secure this value by using [device-local encryption](/guides/local-encryption/).

<Steps>
1. **Create a new service account** and grant access to necessary vault(s). This is a special account used for machine-to-machine communication. This can only be done in the 1Password web interface. Be sure to save the new service account token in another vault so you can find it later. [link](https://www.1password.dev/service-accounts/get-started/)
    :::note[Vault access is set during creation only]
    Vault access rules cannot be edited after creation, so if your vault setup changes, you will need to create new service account(s) and update the tokens.
    :::

2. **Wire up the service account token in your config**. Add a config item of type `opServiceAccountToken` to hold the token value, and reference it when initializing the plugin.

    ```diff lang="env-spec" title=".env.schema" add="token=$OP_TOKEN"
    # @plugin(@varlock/1password-plugin)
    # @initOp(token=$OP_TOKEN)
    # ---
    +# @type=opServiceAccountToken @sensitive @internal
    +OP_TOKEN=
    ```
    :::note[Service account tokens are `@internal` by default]
    The `opServiceAccountToken` type is marked [`@internal`](/reference/item-decorators/#internal), so the token is used by varlock to fetch your other secrets but is **not** injected into your application, keeping this secret-zero out of your app's environment. It still shows (redacted) in `varlock load` output so you can debug it. If your app genuinely needs the token directly, opt out with `@internal=false`.
    :::
3. **Set your service account token in deployed environments**. Copy the token value from where you saved it earlier, and set it in deployed environments using your platform's env var management UI. Be sure to use the same name as you defined in your schema (e.g. `OP_TOKEN`).
</Steps>

:::caution[Ensure service account access is enabled]
Each vault has a toggle to disable service account access _in general_. It is on by default, so you will likely not need to do anything. [link](https://www.1password.dev/service-accounts/manage-service-accounts/)
:::


### CLI-based service account auth (memory-constrained environments)

By default, service account tokens use the 1Password JavaScript SDK (which bundles a WASM module, ~400 MB RSS). In memory-constrained environments such as 512 MB containers or VMs, this footprint may be prohibitive.

Set `useCliWithServiceAccount=true` to use the `op` CLI binary instead of the SDK while still authenticating headlessly via the service account token:

```diff lang="env-spec" title=".env.schema" add="useCliWithServiceAccount=true"
# @plugin(@varlock/1password-plugin)
# @initOp(token=$OP_TOKEN, useCliWithServiceAccount=true)
# ---

# @type=opServiceAccountToken @sensitive @internal
OP_TOKEN=
```

**Requirements:**

1. **Install the `op` CLI**: [Installation guide](https://www.1password.dev/cli/get-started/)
2. The token referenced by `token=` must resolve to a valid service account token at load time.

The `op` binary is significantly lighter than the WASM SDK. Authentication is handled headlessly via `OP_SERVICE_ACCOUNT_TOKEN`, with no desktop app or interactive sign-in needed. Multiple plugin instances configured with different service account tokens are fully isolated from one another.


### Desktop app auth (for local dev)

During local development, you may find it convenient to skip the service account tokens and instead rely on your local 1Password desktop app (via the [CLI integration](https://www.1password.dev/cli/get-started/#step-2-turn-on-the-1password-desktop-app-integration)), including using its biometric unlocking features.

<Steps>
1. **Opt-in while initializing the plugin**
    ```diff lang="env-spec" title='.env.schema' add="allowAppAuth=true"
    # @plugin(@varlock/1password-plugin)
    # @initOp(token=$OP_TOKEN, allowAppAuth=true)
    ```

    You may use other functions to conditionally enable this, for example `forEnv(dev)`.

2. **Specify 1Password account (optional)**
    ```diff lang="env-spec" title='.env.schema' add="account=acmeco"
    # @plugin(@varlock/1password-plugin)
    # @initOp(token=$OP_TOKEN, allowAppAuth=true, account=acmeco)
    ```

    This value is passed through under the `--account` flag to the `op` CLI, and accepts account shorthand, sign-in address, account ID, or user ID.

    You can run `op account list` to see your available accounts. The shorthand is the subdomain of your `x.1password.com` sign-in address.

    This is optional, but recommended if you have access to multiple 1Password accounts, to ensure you connect to the correct one.

3. **Ensure the `op` CLI is installed**. [docs](https://www.1password.dev/cli/get-started/)

4. **Enable the desktop app + CLI integration**. [docs](https://www.1password.dev/cli/get-started/#step-2-turn-on-the-1password-desktop-app-integration)

{/* 5. **Run `op signin` to test**. Ensure you are logged in to the correct account. You can run `op whoami` to see which account is currently connected to the CLI. */}
</Steps>

With this option enabled, if the resolved service account token is empty, we will call out to the `op` cli installed on your machine (it must be in your `$PATH`) and use the auth it provides. With the desktop app integration enabled, it will call out and may trigger biometric verification to unlock.

:::caution[Connecting as yourself]
Keep in mind that this method is connecting as _YOU_ who likely has more access than a tightly scoped service account. Consider only enabling this method for a plugin instance that will be handling non-production secrets.
:::


## Pulling data from 1Password
Once the plugin is installed and initialized, you can start adding config items that load values from 1Password using the `op()` resolver function.

You can wire up individual items to specific fields in by using [1Password secret references](https://www.1password.dev/cli/secret-references/).

```env-spec
DB_PASS=op(op://my-vault/database-password/password)
```

:::tip[Where to find a secret reference]
The secret reference for invidivual fields within an item can be found by clicking on the down arrow icon on the field and selecting `Copy Secret Reference`.
:::

If you have multiple plugin instances, the `op()` function accepts an optional first parameter to specify which instance id to use.

```env-spec /dev|prod/
# @initOp(id=dev, token=$OP_TOKEN_DEV, allowAppAuth=true)
# @initOp(id=prod, token=$OP_TOKEN_PROD, allowAppAuth=false)
# ---
DEV_ITEM=op(dev, op://vault-name/item-name/field-name)
PROD_ITEM=op(prod, op://vault-name/item-name/field-name)
```





### Loading 1Password Environments

Use `opLoadEnvironment()` with `@setValuesBulk` to load all variables from a [1Password environment](https://www.1password.dev/environments/) at once, instead of wiring up each secret individually:

```env-spec title=".env.schema"
# @plugin(@varlock/1password-plugin)
# @initOp(token=$OP_TOKEN, allowAppAuth=forEnv(dev), account=acmeco)
# @setValuesBulk(opLoadEnvironment(your-environment-id))
# ---

# @type=opServiceAccountToken @sensitive @internal
OP_TOKEN=

API_KEY=
DB_PASSWORD=
```

With a named instance:

```env-spec title=".env.schema"
# @initOp(id=prod, token=$OP_TOKEN_PROD, allowAppAuth=false)
# @setValuesBulk(opLoadEnvironment(prod, your-environment-id))
```

:::note[Beta CLI required for desktop app auth]
When using desktop app auth (`allowAppAuth`), the `op environment` command requires a beta version of the 1Password CLI (v2.33.0+). Download it from the [CLI release history](https://app-updates.agilebits.com/product_history/CLI2) (click "show betas"). Service account auth via the SDK does not have this requirement.
:::

----


## Reference

### Root decorators
<div class="reference-docs">
<div>
#### `@initOp()`

Initializes an instance of the 1Password plugin, setting up options and authentication. Can be called multiple times to set up different instances.

**Key/value args:**
- `id` (optional): identifier for this instance, used when multiple instances are needed
- `token` (optional): service account token. Should be a reference to a config item of type `opServiceAccountToken`.
- `allowAppAuth` (optional): boolean flag to enable authenticating using the local desktop app
- `account` (optional): limits the `op` cli to connect to specific 1Password account (shorthand, sign-in address, account ID, or user ID)
- `useCliWithServiceAccount` (optional): when `true`, uses the `op` CLI binary instead of the WASM SDK for service account auth. Useful in memory-constrained environments. Requires the `op` CLI to be installed.
- `cacheTtl` (optional): when set, resolved values from `op()` and `opLoadEnvironment()` are cached for the specified duration. Accepts the same format as the [`cache()` function](/reference/functions/#cache), for example `"5m"`, `"1h"`, `"1d"`, or `forever` to cache until manually cleared. Storage follows global [`@cache`](/reference/root-decorators/#cache) mode (`disk` or `memory`), and is disabled when caching is globally disabled or `--skip-cache` is used. For cache mode strategy and CLI cache controls, see the [Caching guide](/guides/caching/).

```env-spec "@initOp"
# @initOp(id=notProd, token=$OP_TOKEN, allowAppAuth=forEnv(dev), account=acmeco, cacheTtl=1h)
# ---
# @type=opServiceAccountToken
OP_TOKEN=
```
</div>
</div>


### Data types
<div class="reference-docs">
<div>
#### `opServiceAccountToken`

Represents a [1Password service account token](https://www.1password.dev/service-accounts/). Validation ensures the token is in the correct format, and a link to the 1Password docs is added for convenience.
Note that the type itself is marked as `@sensitive`, so adding an explicit `@sensitive` decorator is optional.

```env-spec "opServiceAccountToken"
# @type=opServiceAccountToken
OP_TOKEN=
```
</div>
</div>


### Resolver functions
<div class="reference-docs">
<div>
#### `op()`

Fetches an individual field using a 1Password secret reference

**Array args:**
- `instanceId` (optional): instance identifier to use when multiple plugin instances are initialized
- `secretReference`: secret reference to fetch value from, in the format `op://vault-name/item-name/field-name`


```env-spec /op\\(.*\\)/
ITEM=op(op://vault-name/item-name/field-name)

# example using a plugin instance id
ITEM_WITH_INSTANCE_ID=op(prod, op://vault-name/item-name/field-name)
```
</div>

<div>
#### `opLoadEnvironment()`

Load all variables from a [1Password environment](https://www.1password.dev/environments/). Intended for use with `@setValuesBulk`.

**Array args:**
- `instanceId` (optional): instance identifier to use when multiple plugin instances are initialized
- `environmentId`: the 1Password environment ID to load variables from

```env-spec /opLoadEnvironment\\(.*\\)/
# Load all variables from a 1Password environment
# @setValuesBulk(opLoadEnvironment(your-environment-id))

# With a named instance
# @setValuesBulk(opLoadEnvironment(prod, your-environment-id))
```
</div>
</div>
