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
- 2. Adding a Secret to an Existing Project
- 3. Setting Up a New Project
- 4. Service Account Management
- 5. Connect Server Operations
- 6. Naming Conventions
- 7. Troubleshooting
1. Vault Structure
Current vaults and their purpose:
| Vault | Purpose | Accessed By |
|---|---|---|
CI-SHARED | GHCR, Cloudflare global, SES SMTP | All repos (org SA) + Connect |
CI-AWS | ECR, RDS, Lambda, S3 | All repos (org SA) + Connect |
CI-MYPROJECT | my-project deploy keys, APIs, env vars | SA myproject + Connect |
CI-SATIS | GitHub token, R2, env vars | SA satis |
PRIVATE | JWT keys, A1 certificate | SA myproject + Connect |
CLOUD | Legacy 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
- Open 1Password web → select the appropriate vault
- Create item following naming convention:
{SERVICE}-{context} - 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/fieldIn .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-TMS3.2 Create Service Account
- 1Password web → Developer Tools → Service Accounts
- Name:
sa-github-ci-{project}(lowercase) - 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 prompted3.4 Create initial items
Follow naming convention in section 6. Minimum items for a typical WP project:
| Item | Vault | Purpose |
|---|---|---|
ENV-{project} | CI-{PROJECT} | App env vars (sections per integration) |
SSH-{repo}-deploy-key | CI-{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 Name | GitHub Secret | Vaults | Scope |
|---|---|---|---|
sa-github-ci-shared | OP_SERVICE_ACCOUNT_TOKEN (org) | CI-SHARED, CI-AWS | All repos |
sa-github-ci-myproject | OP_SA_MYPROJECT (repo) | CI-MYPROJECT, PRIVATE | my-project repos |
sa-github-ci-satis | OP_SA_SATIS (repo) | CI-SATIS | my-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
- 1Password web → Developer Tools → Service Accounts → select SA
- Revoke old token, generate new one
- Update GitHub secret:
gh secret set OP_SA_{PROJECT} --repo middag-io/{repo} - 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 .envAdding a vault to Connect
- 1Password web → Integrations → Connect servers → select server
- Add vault → select CI-
- No redeploy needed — Connect picks up vault changes automatically
Restarting Connect
bash
docker compose -f docker-compose.connect.yml restartHealth check
bash
curl -s http://localhost:8080/health | jq .
# Expected: {"state":"ACTIVE"}6. Naming Conventions
Vaults
| Pattern | Example | Use |
|---|---|---|
CI-SHARED | — | Cross-project shared creds |
CI-AWS | — | AWS service creds |
CI-{PROJECT} | CI-MYPROJECT | Project-specific |
PRIVATE | — | Private keys, certificates |
Items: {SERVICE}-{context}
| Pattern | Example | Purpose |
|---|---|---|
GITHUB-{org}-{purpose} | GITHUB-middag-io-GHCR | GitHub tokens |
CLOUDFLARE-{project} | CLOUDFLARE-myproject | CF API/R2/SSL |
AWS-EC2-{repo} | AWS-EC2-docker-wp-myproject | Deploy targets |
SSH-{repo}-{purpose} | SSH-docker-wp-myproject-dev-key | SSH keys |
ENV-{project} | ENV-myproject | App env vars |
{SERVICE} | HUBSPOT, STRIPE | External APIs |
Sections (within items)
Group fields by concern. English, short:
Credentials— username, password, tokenEC2— host, user, folderS3-Credentials— access key, secret, bucketORIGIN-SSL— certificate, private_keyBASE64— base64-encoded keys/certs
Fields
snake_casefor 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-staging7. Troubleshooting
CI (GitHub Actions)
| Problem | Cause | Fix |
|---|---|---|
could not find item | Wrong vault/item name in op:// | Verify in 1Password web |
unauthorized | SA doesn't have vault access | Check SA permissions |
permission denied | Using org SA for project vault | Use project-specific SA |
Connect (Server)
| Problem | Cause | Fix |
|---|---|---|
connect: connection refused | Connect not running | docker compose up -d op-connect-api op-connect-sync |
item not found | Vault not added to Connect | 1Password web → Integrations → add vault |
op inject hangs | OP_CONNECT_HOST not set | Export OP_CONNECT_HOST=http://op-connect-api:8080 |