Skip to content

Kody is live

Watch the launch video — what Kody is, and why it exists.

← Public packages

@kentcdodds/onepassword

Resolve 1Password Connect item fields for secret-aware fetch with website host allowlisting.

connect/deployment-guide.ts

115 lines · 5.4 KB · TypeScript
/**
 * Return markdown deployment instructions for 1Password Connect on Fly.io.
 *
 * Use this deployment guide when a Kody workflow needs Connect-on-Fly setup steps,
 * required Kody secret names, and bind checklist without secret values.
 */
export default async function deploymentGuide() {
	return [
		'# 1Password Connect on Fly — deployment guide',
		'',
		'## Overview',
		'',
		'Deploy official images `1password/connect-api:latest` and',
		'`1password/connect-sync:latest` as a **multi-container Fly Machine**.',
		'Fly HTTPS terminates at the proxy and forwards to connect-api `:8080`.',
		'',
		'Default app name: `kody-onepassword-connect`',
		'Default URL: `https://kody-onepassword-connect.fly.dev`',
		'',
		'## 1. Create Connect server credentials (1Password Developer)',
		'',
		'1. In 1Password → Developer → Connect servers, create a server.',
		'2. Download `1password-credentials.json`.',
		'3. Create a Connect access token with read access to the vaults you need.',
		'4. Base64-encode the credentials file locally (Machines `config.files.raw_value` expects Base64; container `OP_SESSION` is the file path,',
		'   not raw JSON). Example: `base64 -w0 1password-credentials.json` (Linux) or',
		'   `base64 < 1password-credentials.json | tr -d \'\\n\'` (macOS).',
		'',
		'## 2. Save Kody secrets (never paste values into chat)',
		'',
		'### OP_SESSION (required for deploy-fly)',
		'',
		'[Save ONEPASSWORD_CONNECT_OP_SESSION](https://kody.codes/account/secrets/new?name=ONEPASSWORD_CONNECT_OP_SESSION&description=Base64%20encoding%20of%201password-credentials.json%20(not%20raw%20JSON).%20Fly%20Connect%20reads%20this%20as%20OP_SESSION%20via%20connect%2Fdeploy-fly%20setSecrets.&allowedHosts=api.machines.dev%2Capi.fly.io&scope=user)',
		'',
		'Store **Base64(credentials.json)** — not the raw JSON file contents.',
		'`./connect/deploy-fly` mounts Base64 via `config.files` and cannot Base64 inside package code without reading the',
		'secret, so the value must already be Base64 when saved.',
		'',
		'Approve hosts **`api.machines.dev`** and **`api.fly.io`** on this secret',
		'(placeholders expand only inside secret-aware `fetch` to those Fly APIs).',
		'Package grant for `@kentcdodds/onepassword` should already be set; if deploy',
		'complains about grants, add the package on the secret.',
		'',
		'### Legacy name (do not use raw JSON as OP_SESSION)',
		'',
		'If you previously saved raw JSON as `ONEPASSWORD_CONNECT_CREDENTIALS_JSON`,',
		'do **not** point deploy at it. Re-save Base64 under',
		'`ONEPASSWORD_CONNECT_OP_SESSION` instead. Deploy-fly will fail closed rather',
		'than silently send raw JSON as Fly `OP_SESSION`.',
		'',
		'### Connect token (door key + smoke)',
		'',
		'[Save / update ONEPASSWORD_CONNECT_TOKEN](https://kody.codes/account/secrets/new?name=ONEPASSWORD_CONNECT_TOKEN&description=1Password%20Connect%20Server%20token%20(Authorization%3A%20Bearer)%20used%20as%20the%20door%20key%20for%20the%201password%20secret%20provider%20and%20API%20smoke%20tests&allowedHosts=kody-onepassword-connect.fly.dev&scope=user)',
		'',
		'If the secret already exists, edit it and set `allowedHosts` to include',
		'`kody-onepassword-connect.fly.dev` (and any other Connect hosts you call).',
		'',
		'### flyApiToken',
		'',
		'`flyApiToken` should already exist for `@kentcdodds/fly` / Discord Fly deploys.',
		'It must allow hosts **`api.machines.dev`** and **`api.fly.io`**:',
		'',
		'[Update flyApiToken hosts](https://kody.codes/account/secrets/new?name=flyApiToken&description=Fly.io%20API%20token%20for%20Machines%20API%20and%20GraphQL%20(api.machines.dev%20%2B%20api.fly.io)&allowedHosts=api.machines.dev%2Capi.fly.io%2Cdocs.machines.dev%2Cfly.io&scope=user)',
		'',
		'## 3. Deploy',
		'',
		'```js',
		"import deploy from 'kody:@kentcdodds/onepassword/connect/deploy-fly'",
		'const result = await deploy()',
		'// => { ok: true, appName, url, region, ... }  (no secret values)',
		'```',
		'',
		'Secret placeholders appear only inside outbound Fly `fetch` calls',
		'(`Authorization: Bearer {{secret:flyApiToken}}` and JSON body',
		'`files.raw_value: {{secret:ONEPASSWORD_CONNECT_OP_SESSION}}`), matching the',
		'Discord `./proxy/deploy-fly` pattern. Module-level transforms of secret',
		'strings are not expanded.',
		'',
		'Or retrieve files only:',
		'',
		'```js',
		"import source from 'kody:@kentcdodds/onepassword/connect/source'",
		'const bundle = await source()',
		'```',
		'',
		'## 4. Smoke',
		'',
		'- `GET https://kody-onepassword-connect.fly.dev/heartbeat` (unauthenticated)',
		'- `GET https://kody-onepassword-connect.fly.dev/v1/vaults` with',
		'  `Authorization: Bearer {{secret:ONEPASSWORD_CONNECT_TOKEN}}`',
		'',
		'## 5. Bind sealed provider',
		'',
		'```js',
		"import { kody } from 'kody:runtime'",
		'',
		'export default async function main() {',
		'  return await kody.secretProviderBind({',
		"    provider: '1password',",
		"    package_id: '7603a38f-6d3f-44e4-beca-b10196b38008',",
		"    door_secret_name: 'ONEPASSWORD_CONNECT_TOKEN',",
		"    config: { connectHost: 'https://kody-onepassword-connect.fly.dev' },",
		'  })',
		'}',
		'```',
		'',
		'## Shape details',
		'',
		'- Shared volume mount: `/home/opuser/.op/data` (Machine `temp_dir` named `opdata`)',
		'- Credentials via Fly secret `OP_SESSION` = value of `ONEPASSWORD_CONNECT_OP_SESSION` (Base64)',
		'- Manual bus (no NET_BROADCAST): API `11223` ↔ sync `11224` on `localhost`',
		'- Sync uses `OP_HTTP_PORT=8081` so it does not collide with API `8080`',
		'',
	].join('\n')
}