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/source.ts

227 lines · 6.7 KB · TypeScript
const appNameDefault = 'kody-onepassword-connect'
const regionDefault = 'ams'
const credentialsPath = '/home/opuser/.op/1password-credentials.json'
const dataMountPath = '/opdata'

const dockerCompose = [
	'version: "3.4"',
	'',
	'services:',
	'  connect-sync:',
	'    image: 1password/connect-sync:latest',
	'    environment:',
	`      OP_SESSION: "${credentialsPath}"`,
	`      XDG_DATA_HOME: "${dataMountPath}"`,
	'      OP_HTTP_PORT: "8081"',
	'      OP_BUS_PORT: "11221"',
	'      OP_BUS_PEERS: "localhost:11220"',
	'    volumes:',
	`      - "./1password-credentials.json:${credentialsPath}"`,
	`      - "opdata:${dataMountPath}"`,
	'  connect-api:',
	'    image: 1password/connect-api:latest',
	'    ports:',
	'      - "8080:8080"',
	'    environment:',
	`      OP_SESSION: "${credentialsPath}"`,
	`      XDG_DATA_HOME: "${dataMountPath}"`,
	'      OP_HTTP_PORT: "8080"',
	'      OP_BUS_PORT: "11220"',
	'      OP_BUS_PEERS: "localhost:11221"',
	'      OP_SYNC_TIMEOUT: "60s"',
	'    volumes:',
	`      - "./1password-credentials.json:${credentialsPath}"`,
	`      - "opdata:${dataMountPath}"`,
	'',
	'volumes:',
	'  opdata:',
	'',
].join('\n')

const flyToml = [
	`app = "${appNameDefault}"`,
	`primary_region = "${regionDefault}"`,
	'',
	'[build]',
	'  # Prefer Machines API multi-container deploy via ./connect/deploy-fly.',
	'  # Official images: 1password/connect-api:latest + 1password/connect-sync:latest',
	'',
	'[http_service]',
	'  internal_port = 8080',
	'  force_https = true',
	'  auto_stop_machines = false',
	'  auto_start_machines = true',
	'  min_machines_running = 1',
	'',
	'[[vm]]',
	'  size = "shared-cpu-1x"',
	'  memory = "1024mb"',
	'  cpus = 1',
	'',
	'[mounts]',
	'  source = "opdata"',
	`  destination = "${dataMountPath}"`,
	'',
].join('\n')

const machineConfigJson = `${JSON.stringify(
	{
		region: regionDefault,
		config: {
			guest: { cpu_kind: 'shared', cpus: 1, memory_mb: 1024 },
			files: [
				{
					guest_path: credentialsPath,
					raw_value:
						'<Base64(1password-credentials.json) from ONEPASSWORD_CONNECT_OP_SESSION>',
				},
			],
			containers: [
				{
					name: 'connect-sync',
					image: '1password/connect-sync:latest',
					env: {
						OP_SESSION: credentialsPath,
						XDG_DATA_HOME: dataMountPath,
						OP_HTTP_PORT: '8081',
						OP_BUS_PORT: '11221',
						OP_BUS_PEERS: 'localhost:11220',
					},
					mounts: [{ volume: '<opdata volume id>', path: dataMountPath }],
				},
				{
					name: 'connect-api',
					image: '1password/connect-api:latest',
					env: {
						OP_SESSION: credentialsPath,
						XDG_DATA_HOME: dataMountPath,
						OP_HTTP_PORT: '8080',
						OP_BUS_PORT: '11220',
						OP_BUS_PEERS: 'localhost:11221',
						OP_SYNC_TIMEOUT: '60s',
					},
					mounts: [{ volume: '<opdata volume id>', path: dataMountPath }],
					healthchecks: [
						{
							name: 'api-heartbeat',
							http: {
								port: 8080,
								method: 'GET',
								path: '/heartbeat',
								scheme: 'http',
							},
							grace_period: 90,
							interval: 15,
							timeout: 5,
							success_threshold: 1,
							failure_threshold: 5,
						},
					],
				},
			],
			services: [
				{
					protocol: 'tcp',
					internal_port: 8080,
					autostart: true,
					autostop: false,
					ports: [
						{ port: 80, handlers: ['http'], force_https: true },
						{ port: 443, handlers: ['tls', 'http'] },
					],
				},
			],
		},
	},
	null,
	2,
)}\n`

const readme = [
	'# Kody 1Password Connect (Fly)',
	'',
	'Runs official `1password/connect-api` + `1password/connect-sync` on one Fly Machine',
	'(multi-container). Credentials are a **file** (`OP_SESSION` = path). SQLite data lives on',
	`persistent volume \`opdata\` mounted at \`${dataMountPath}\` via \`XDG_DATA_HOME\` so`,
	'`/home/opuser/.op` stays owned by `opuser`. HTTPS via Fly proxy → connect-api:8080.',
	'',
	'## Required secrets (Kody names)',
	'',
	'- `ONEPASSWORD_CONNECT_OP_SESSION` — Base64(`1password-credentials.json`) (no newlines).',
	'  deploy-fly writes this via Machines `config.files.raw_value` and sets container env',
	`  \`OP_SESSION=${credentialsPath}\` (path, not Base64).`,
	'- `flyApiToken` — Machines API + GraphQL for app/machine/IP/volumes.',
	'',
	'Client / sealed provider door key (not a Connect container env):',
	'',
	'- `ONEPASSWORD_CONNECT_TOKEN` — Bearer token for `/v1/*` and `secretProviderBind`.',
	'',
	'## Deploy',
	'',
	'```js',
	"import deploy from 'kody:@kentcdodds/onepassword/connect/deploy-fly'",
	'await deploy()',
	'```',
	'',
	`Default app: \`${appNameDefault}\`\`https://${appNameDefault}.fly.dev\`.`,
	'',
].join('\n')

const files: Record<string, string> = {
	'docker-compose.yaml': dockerCompose,
	'fly.toml': flyToml,
	'machine-config.json': machineConfigJson,
	'README.md': readme,
	'NOTES.md': [
		'# Notes',
		'',
		'- Fly multi-container Machines share a network namespace → use localhost peers',
		'  with distinct `OP_BUS_PORT` values (11220 / 11221) and distinct `OP_HTTP_PORT`.',
		'- Prefer `./connect/deploy-fly` (Machines API) over flyctl; no custom Dockerfile.',
		`- Shared data uses named temp_dir volume \`${dataMountPath}\` (\`XDG_DATA_HOME\`), not temp_dir`,
		'  and not `/home/opuser/.op/data` (volume ownership breaks ConfigDir checks).',
		`- Credentials file at \`${credentialsPath}\`; \`OP_SESSION\` is that path.`,
		'- Do not set Fly app secret `OP_SESSION` to Base64 — that overrides the path env.',
		'- Sync starts first (no depends_on api). API uses `OP_SYNC_TIMEOUT=60s`.',
		'',
	].join('\n'),
}

/**
 * Default callable for `kody:@kentcdodds/onepassword/connect/source`.
 *
 * Use this source entrypoint when a Kody workflow needs deployable 1Password Connect
 * Fly/Docker files (compose, machine-config, fly.toml) without secret values.
 */
export default async function getOnepasswordConnectSource() {
	return {
		name: appNameDefault,
		description:
			'Fly multi-container 1Password Connect Server (connect-api + connect-sync) for Kody sealed secret provider.',
		files,
		env: {
			required: ['OP_SESSION'],
			optional: [
				'OP_HTTP_PORT',
				'OP_BUS_PORT',
				'OP_BUS_PEERS',
				'OP_LOG_LEVEL',
				'OP_SYNC_TIMEOUT',
				'XDG_DATA_HOME',
			],
			kodySecrets: [
				'ONEPASSWORD_CONNECT_OP_SESSION',
				'flyApiToken',
				'ONEPASSWORD_CONNECT_TOKEN',
			],
		},
		deployNotes: [
			'Use kody:@kentcdodds/onepassword/connect/deploy-fly for Machines API deploy.',
			'Save ONEPASSWORD_CONNECT_OP_SESSION as Base64(credentials.json) before deploy.',
			'Deploy mounts credentials via config.files and sets OP_SESSION to the file path.',
			'Shared temp_dir opdata at /opdata (XDG_DATA_HOME); unset Fly OP_SESSION env secret.',
			'HTTPS terminates at Fly proxy → connect-api internal 8080.',
			'Bind sealed provider with connectHost https://kody-onepassword-connect.fly.dev after smoke.',
		],
	}
}