Skip to content

1Password Operations Guide — middag-io

Day-to-day vault management, secret creation, Service Account setup, and Connect operations. Architecture decisions: see ADR-005.

Table of Contents


1. Vault Structure

Current vaults and their purpose:

VaultPurposeAccessed By
CI-SHAREDGHCR, Cloudflare global, SES SMTPAll repos (org SA) + Connect
CI-AWSECR, RDS, Lambda, S3All repos (org SA) + Connect
CI-MYPROJECTmy-project deploy keys, APIs, env varsSA myproject + Connect
CI-SATISGitHub token, R2, env varsSA satis
PRIVATEJWT keys, A1 certificateSA myproject + Connect
CLOUDLegacy Cloudflare (migrating to CI-*)Connect only

Decision Tree: Which Vault?

Is it shared across projects?
├── Yes → Is it AWS?
│         ├── Yes → CI-AWS
│         └── No → CI-SHARED
└── No → Is it a private key or certificate?
         ├── Yes → PRIVATE
         └── No → CI-{PROJECT}

2. Adding a Secret to an Existing Project

Step 1: Create the item in 1Password

  1. Open 1Password web → select the appropriate vault
  2. Create item following naming convention: {SERVICE}-{context}
  3. Add fields in appropriate sections

Step 2: Reference in code

In GitHub Actions workflow:

yaml
- uses: 1password/load-secrets-action@v2
  with:
    export-env: true
  env:
    OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SA_MYPROJECT }}
    MY_SECRET: op://CI-MYPROJECT/item-name/section/field

In .env template (server):

MY_SECRET="op://CI-MYPROJECT/item-name/section/field"

In auth.json.tpl (Composer):

json
{
  "http-basic": {
    "privatesatis.middag.com.br": {
      "username": "op://CI-MYPROJECT/ENV-myproject/PRIVATE-SATIS/SATIS_USER",
      "password": "op://CI-MYPROJECT/ENV-myproject/PRIVATE-SATIS/SATIS_PASSWORD"
    }
  }
}

Step 3: Verify access

  • CI: Run workflow on develop branch to verify secret loads
  • Server: op inject -f -i .env.production.tpl -o /dev/stdout | grep MY_SECRET

3. Setting Up a New Project

Complete procedure for adding 1Password integration to a new repo.

3.1 Create the vault

Vault name: CI-{PROJECT} (uppercase)
Example: CI-HELICO, CI-TMS

3.2 Create Service Account

  1. 1Password web → Developer Tools → Service Accounts
  2. Name: sa-github-ci-{project} (lowercase)
  3. Grant access to vaults:
    • CI-{PROJECT} (project-specific)
    • CI-SHARED (if needs GHCR, Cloudflare, SES)
    • CI-AWS (if needs ECR, RDS, S3)
    • PRIVATE (if needs JWT keys, certificates)

3.3 Add GitHub secret

bash
gh secret set OP_SA_{PROJECT} --repo middag-io/{repo-name}
# Paste the SA token when prompted

3.4 Create initial items

Follow naming convention in section 6. Minimum items for a typical WP project:

ItemVaultPurpose
ENV-{project}CI-{PROJECT}App env vars (sections per integration)
SSH-{repo}-deploy-keyCI-{PROJECT}SSH key for server deploy
AWS-EC2-{repo}CI-{PROJECT}Deploy target (host, user, folder)

3.5 Wire up in workflow

Use 1password/load-secrets-action@v2 in the repo's GitHub Actions workflows.


4. Service Account Management

Current Service Accounts

SA NameGitHub SecretVaultsScope
sa-github-ci-sharedOP_SERVICE_ACCOUNT_TOKEN (org)CI-SHARED, CI-AWSAll repos
sa-github-ci-myprojectOP_SA_MYPROJECT (repo)CI-MYPROJECT, PRIVATEmy-project repos
sa-github-ci-satisOP_SA_SATIS (repo)CI-SATISmy-satis-repo

When to create a new SA

  • New project with its own CI-{PROJECT} vault
  • Never share SA tokens across unrelated projects

Rotating a SA token

  1. 1Password web → Developer Tools → Service Accounts → select SA
  2. Revoke old token, generate new one
  3. Update GitHub secret: gh secret set OP_SA_{PROJECT} --repo middag-io/{repo}
  4. Re-run CI to verify

5. Connect Server Operations

Architecture

EC2 host
├── op-connect-api (port 8080, localhost only)
├── op-connect-sync
└── App containers
    └── op inject -f -i .env.production.tpl -o .env

Adding a vault to Connect

  1. 1Password web → Integrations → Connect servers → select server
  2. Add vault → select CI-
  3. No redeploy needed — Connect picks up vault changes automatically

Restarting Connect

bash
docker compose -f docker-compose.connect.yml restart

Health check

bash
curl -s http://localhost:8080/health | jq .
# Expected: {"state":"ACTIVE"}

6. Naming Conventions

Vaults

PatternExampleUse
CI-SHAREDCross-project shared creds
CI-AWSAWS service creds
CI-{PROJECT}CI-MYPROJECTProject-specific
PRIVATEPrivate keys, certificates

Items: {SERVICE}-{context}

PatternExamplePurpose
GITHUB-{org}-{purpose}GITHUB-middag-io-GHCRGitHub tokens
CLOUDFLARE-{project}CLOUDFLARE-myprojectCF API/R2/SSL
AWS-EC2-{repo}AWS-EC2-docker-wp-myprojectDeploy targets
SSH-{repo}-{purpose}SSH-docker-wp-myproject-dev-keySSH keys
ENV-{project}ENV-myprojectApp env vars
{SERVICE}HUBSPOT, STRIPEExternal APIs

Sections (within items)

Group fields by concern. English, short:

  • Credentials — username, password, token
  • EC2 — host, user, folder
  • S3-Credentials — access key, secret, bucket
  • ORIGIN-SSL — certificate, private_key
  • BASE64 — base64-encoded keys/certs

Fields

  • snake_case for new fields
  • Existing Portuguese/spaced names: update when item is next touched

1Password Environments

For op run --env on servers:

{project}-{environment}
Example: myproject-production, myproject-staging

7. Troubleshooting

CI (GitHub Actions)

ProblemCauseFix
could not find itemWrong vault/item name in op://Verify in 1Password web
unauthorizedSA doesn't have vault accessCheck SA permissions
permission deniedUsing org SA for project vaultUse project-specific SA

Connect (Server)

ProblemCauseFix
connect: connection refusedConnect not runningdocker compose up -d op-connect-api op-connect-sync
item not foundVault not added to Connect1Password web → Integrations → add vault
op inject hangsOP_CONNECT_HOST not setExport OP_CONNECT_HOST=http://op-connect-api:8080

MIDDAG Tecnologia