@kentcdodds/onepassword
Resolve 1Password Connect item fields for secret-aware fetch with website host allowlisting.
AGENTS.md
107 lines · 5.2 KB · Markdown@kentcdodds/onepassword — agent notes
Human intent and setup live in README.md.
Exports
| Subpath | Use |
|---|---|
. | Overview / bind checklist / Connect-on-Fly pointers. Safe to import from execute. |
./secretProvider | Sealed. Platform fetch boundary only. Do not kody:@ or invoke. |
./connect | Describe Connect-on-Fly sub-exports. |
./connect/source | Deployable compose / machine-config / fly.toml (no secrets). |
./connect/deployment-guide | Markdown deploy + bind steps. |
./connect/deploy-fly | Machines API create/update Fly Connect app. Needs secrets approved. |
Overview smoke
import onepassword from 'kody:@kentcdodds/onepassword'
export default async function main() {
return await onepassword()
}Connect on Fly
- Check
secretListforONEPASSWORD_CONNECT_OP_SESSIONbefore deploy. - If missing, give Kent the prefilled URL — do not invent credentials or deploy.
- Value must be Base64(
1password-credentials.json), not raw JSON. Package code cannot Base64 without reading the secret; placeholders only expand inside secret-awarefetch. - If only
ONEPASSWORD_CONNECT_CREDENTIALS_JSONexists (often raw JSON), do not fall back — tell Kent to save Base64 asONEPASSWORD_CONNECT_OP_SESSION. - Host allowlist on
ONEPASSWORD_CONNECT_OP_SESSIONandflyApiToken:api.machines.dev,api.fly.io. Package grant for this package is usually already done. - App name default:
kody-onepassword-connect(check@kentcdodds/fly/appsfor collisions). - Deploy with Machines API helper (not flyctl):
import deploy from 'kody:@kentcdodds/onepassword/connect/deploy-fly'
export default async function main() {
return await deploy()
}Returns { appName, url, region, bind, ... } — never secret values.
Deploy mounts Base64 credentials via Machines config.files.raw_value from
{{secret:ONEPASSWORD_CONNECT_OP_SESSION|scope=user}} to
/home/opuser/.op/1password-credentials.json, sets container env OP_SESSION to
that path, attaches shared temp_dir opdata at /opdata (XDG_DATA_HOME; Fly cannot share one persistent volume across two containers — Connect re-syncs),
unsets any Fly app secret named OP_SESSION (so Base64 is not injected as env),
starts sync before api, bus 11220/11221, OP_SYNC_TIMEOUT=60s. Default memory 1024mb.
Prefills:
- OP_SESSION (Base64 credentials file bytes for config.files):
https://kody.codes/account/secrets/new?name=ONEPASSWORD_CONNECT_OP_SESSION&description=Base64%20of%201password-credentials.json%20(no%20newlines).%20deploy-fly%20writes%20it%20via%20Machines%20config.files%20raw_value%3B%20OP_SESSION%20env%20is%20the%20credentials%20file%20path.&allowedHosts=api.machines.dev%2Capi.fly.io&scope=user - 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 - Token (include Fly host in
allowedHosts):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
Binding (after the door secret exists)
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' },
})
}Accepts connectHost, connectUrl, or baseUrl (normalized, no trailing
slash). Auth header is Authorization: Bearer <doorSecretValue> from invoke
args — never call secret_get for the door key inside the provider.
Resolve / canonicalize contract
canonicalize→{ canonicalRef }only (local id forms; no value).resolve→{ value, hosts, canonicalRef? }.- Empty
hostsmust throw before return (fail closed). - Hosts from
item.urls[].hrefand URL-typed/labeled fields (Secure Notes). - Never log
doorSecretValueorvalue. - Budgets: stay under ~8s resolve / ~5s canonicalize.
Grants
secretProviderLock only accepts locally canonicalizable refs (UUID or
26-char Connect item id ^[\da-z]{26}$ in the item segment). Prefer
i/<item-id>/<field> (Connect ids are usually 26-char, not UUID).
Edge cases
- Scan vaults with
GET /v1/vaults, thenGET /v1/vaults/{vaultId}/items/{itemId}until HTTP 200 (skip 404). - Missing field / missing item / bad token → clear next-action errors, no secret material in messages.
- Name-based
op://Vault/Item/field→ clear error asking for item-id form (lock/grant stay local; this package does not crawl titles to canonicalize). - Secure Notes: websites list may be empty — require a Website or a URL field pointing at the fetch host so resolve can allowlist hosts.
- Fly multi-container shares a network namespace: distinct
OP_HTTP_PORT/OP_BUS_PORTandlocalhostpeers (no NET_BROADCAST).