feat: backup appwritefix: CORS error by adding platforms
This commit is contained in:
@@ -40,4 +40,17 @@ File is now only downloaded if absent. Upgrade playbook handles re-downloads.
|
||||
- Appwrite console loads without error ✅
|
||||
- Stack running on bab1.mgmt.toal.ca ✅
|
||||
- install_appwrite.yml is idempotent ✅
|
||||
- node_exporter install: complete, metrics confirmed ✅
|
||||
- node_exporter install: complete, metrics confirmed ✅
|
||||
- bootstrap_appwrite.yml: project + API key creation working ✅
|
||||
- API key stored at kv/oys/bab-appwrite-api-key
|
||||
|
||||
## bootstrap_appwrite.yml — Key Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| No account creation task | Appwrite only grants console owner role via web UI signup, not REST API |
|
||||
| JWT required for console API | Session cookie alone gives `role: users`; JWT carries team membership claims including `projects.write` |
|
||||
| teamId fetched dynamically | Appwrite 1.8.x requires teamId in POST /v1/projects; use teams[0]['$id'] from GET /v1/teams |
|
||||
| `$id` via bracket notation | Jinja2 treats `$` as special; dot notation fails |
|
||||
| vault_kv2_write (not vault_kv2_put) | No put module in community.hashi_vault; no patch operation — dedicated path avoids clobbering other secrets |
|
||||
| Dedicated Vault path kv/oys/bab-appwrite-api-key | Separate from env config secrets to avoid full-overwrite on re-run |
|
||||
110
docs/summaries/handoff-2026-03-14-appwrite-bootstrap-backup.md
Normal file
110
docs/summaries/handoff-2026-03-14-appwrite-bootstrap-backup.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Session Handoff: Appwrite Bootstrap, Backup, and Bug Fixes
|
||||
**Date:** 2026-03-14
|
||||
**Session Duration:** ~3 hours
|
||||
**Session Focus:** Fix Appwrite console crash, add bootstrap and backup playbooks
|
||||
**Context Usage at Handoff:** ~85%
|
||||
|
||||
---
|
||||
|
||||
## What Was Accomplished
|
||||
|
||||
1. Fixed `_APP_DOMAIN_TARGET_CNAME` null crash → `playbooks/templates/appwrite.env.j2`
|
||||
2. Fixed idempotency: removed `force: true` from compose download → `playbooks/install_appwrite.yml`
|
||||
3. Fixed `appwrite_response_format` undefined error → `playbooks/provision_database.yml`, `playbooks/provision_users.yml`
|
||||
4. Created Appwrite bootstrap playbook → `playbooks/bootstrap_appwrite.yml`
|
||||
5. Created Appwrite backup playbook → `playbooks/backup_appwrite.yml`
|
||||
6. Diagnosed nginx CORS 405 on `apidev.bab.toal.ca` — **not fixed, open**
|
||||
7. Written decision record → `docs/summaries/decisions-2026-03-14-domain-target-fix.md`
|
||||
|
||||
---
|
||||
|
||||
## Exact State of Work in Progress
|
||||
|
||||
- **CORS / nginx**: `apidev.bab.toal.ca` returns HTTP 405 on OPTIONS preflight from nginx/1.20.1. Root cause: nginx config does not pass OPTIONS to backend. `appwrite.toal.ca` works fine. nginx config is managed by `nginxinc.nginx_core` role; no config templates exist in this repo yet.
|
||||
- **backup_appwrite.yml**: Written and structurally correct but **not yet run successfully end-to-end**. Needs a test run and restore verification.
|
||||
|
||||
---
|
||||
|
||||
## Decisions Made This Session
|
||||
|
||||
| Decision | Rationale | Status |
|
||||
|----------|-----------|--------|
|
||||
| `_APP_DOMAIN_TARGET_CNAME` replaces `_APP_DOMAIN_TARGET` | Deprecated since Appwrite 1.7.0; compose `environment:` blocks list the new var, not the old one — old var silently never reached containers | CONFIRMED |
|
||||
| `appwrite_response_format \| default('1.6')` | Var undefined at module_defaults evaluation time; `1.6` is correct format for Appwrite 1.8.x | CONFIRMED |
|
||||
| bootstrap: no account creation task | Appwrite only grants console `owner` role via web UI signup; REST API creates `role: users` which lacks `projects.write` | CONFIRMED |
|
||||
| bootstrap: JWT required for console API | Session cookie alone gives `role: users`; JWT carries team membership claims including `projects.write` | CONFIRMED |
|
||||
| bootstrap: `teamId` fetched from `GET /v1/teams` | Required field in `POST /v1/projects` for Appwrite 1.8.x; discovered from browser network capture | CONFIRMED |
|
||||
| bootstrap: `['$id']` bracket notation | Jinja2 rejects `.$id` — `$` is a special character | CONFIRMED |
|
||||
| bootstrap: `vault_kv2_write` at `kv/oys/bab-appwrite-api-key` | `vault_kv2_put` does not exist; no PATCH operation — dedicated path avoids full-overwrite of other secrets | CONFIRMED |
|
||||
| backup: mysqldump runs while service UP | `--single-transaction` gives consistent InnoDB snapshot; service must be up for `docker compose exec` | CONFIRMED |
|
||||
| backup: `block/rescue/always` | Ensures `systemctl start appwrite` fires even if volume backup fails | CONFIRMED |
|
||||
|
||||
---
|
||||
|
||||
## Key Numbers
|
||||
|
||||
- `appwrite_response_format` default: `1.6`
|
||||
- Vault path for API key: `kv/oys/bab-appwrite-api-key`, key: `appwrite_api_key`
|
||||
- Backup destination: `/var/backups/appwrite/YYYYMMDDTHHMMSS/`
|
||||
- Volumes backed up (8): `appwrite-uploads`, `appwrite-functions`, `appwrite-builds`, `appwrite-sites`, `appwrite-certificates`, `appwrite-config`, `appwrite-cache`, `appwrite-redis`
|
||||
- Volume excluded: `appwrite-mariadb` (covered by mysqldump)
|
||||
|
||||
---
|
||||
|
||||
## Conditional Logic Established
|
||||
|
||||
- IF `appwrite_compose_project` not set THEN `_compose_project` defaults to `basename(appwrite_dir)` = `appwrite` → Docker volume names are `appwrite_appwrite-uploads`, etc.
|
||||
- IF bootstrap re-run THEN second API key created AND Vault entry overwritten — delete old key from console manually
|
||||
- IF backup fails during volume tar THEN `always` block restarts Appwrite — playbook exits failed, partial backup remains in `backup_dir`
|
||||
|
||||
---
|
||||
|
||||
## Files Created or Modified
|
||||
|
||||
| File Path | Action | Description |
|
||||
|-----------|--------|-------------|
|
||||
| `playbooks/templates/appwrite.env.j2` | Modified | Replaced `_APP_DOMAIN_TARGET` with `_APP_DOMAIN_TARGET_CNAME`; added `_APP_DOMAIN_TARGET_CAA` |
|
||||
| `playbooks/install_appwrite.yml` | Modified | Removed `force: true` from `get_url` |
|
||||
| `playbooks/provision_database.yml` | Modified | `appwrite_response_format \| default('1.6')`; fixed long URL line |
|
||||
| `playbooks/provision_users.yml` | Modified | `appwrite_response_format \| default('1.6')` |
|
||||
| `playbooks/bootstrap_appwrite.yml` | Created | Session→JWT→teams→project→API key→Vault |
|
||||
| `playbooks/backup_appwrite.yml` | Created | mysqldump + volume tar + .env, block/rescue/always |
|
||||
| `docs/summaries/decisions-2026-03-14-domain-target-fix.md` | Created | Decision record for domain var fix and idempotency |
|
||||
| `CLAUDE.md` | Modified | Added trailing newline rule |
|
||||
|
||||
---
|
||||
|
||||
## What the NEXT Session Should Do
|
||||
|
||||
1. **Fix nginx CORS** — `apidev.bab.toal.ca` returns 405 on OPTIONS. Load `playbooks/install_nginx.yml`; find where `nginxinc.nginx_core.nginx_config` vars are defined in inventory and add OPTIONS passthrough.
|
||||
2. **Test backup end-to-end** — run `ansible-navigator run playbooks/backup_appwrite.yml --mode stdout`, verify 8 volume tarballs + `mariadb-dump.sql` + `.env` in `/var/backups/appwrite/<timestamp>/`
|
||||
3. **Validate volume name prefix** — run `docker volume ls | grep appwrite` on bab1 to confirm prefix is `appwrite_`
|
||||
|
||||
---
|
||||
|
||||
## Open Questions Requiring User Input
|
||||
|
||||
- [ ] **CORS fix scope**: Should nginx config live in this repo as templates, or managed elsewhere? — impacts `install_nginx.yml` completion
|
||||
- [ ] **Backup retention**: No rotation yet — each run adds a timestamped dir. Add cleanup task?
|
||||
- [ ] **Backup offsite**: 3-2-1 rule — is S3/rsync in scope?
|
||||
|
||||
---
|
||||
|
||||
## Assumptions That Need Validation
|
||||
|
||||
- ASSUMED: Docker Compose project name for volumes is `appwrite` (basename of `/home/ptoal/appwrite`) — validate with `docker volume ls`
|
||||
- ASSUMED: `teams[0]` in bootstrap is always the admin's personal team — valid only if admin has one team
|
||||
|
||||
---
|
||||
|
||||
## What NOT to Re-Read
|
||||
|
||||
- `docs/summaries/handoff-2026-03-14-appwrite-setup-final.md` — superseded; moved to archive
|
||||
|
||||
---
|
||||
|
||||
## Files to Load Next Session
|
||||
|
||||
- `playbooks/install_nginx.yml` — if working on CORS fix
|
||||
- `playbooks/backup_appwrite.yml` — if testing/fixing backup
|
||||
- `docs/context/architecture.md` — for Appwrite API or EDA work
|
||||
@@ -1,84 +0,0 @@
|
||||
# Session Handoff: Appwrite Stack Setup & Infrastructure Hardening
|
||||
**Date:** 2026-03-14
|
||||
**Session Duration:** ~4 hours
|
||||
**Session Focus:** Bring Appwrite stack to production-ready state on bab1.mgmt.toal.ca
|
||||
**Context Usage at Handoff:** ~70%
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
The install playbook is ready to run. All open questions from the session are resolved. The stack on bab1 is running but with an unpatched compose (no proxyProtocol, old entrypoint issue). **One run of the playbook will bring everything current.**
|
||||
|
||||
---
|
||||
|
||||
## What Was Accomplished This Session
|
||||
|
||||
1. Appwrite `.env` Jinja2 template → `playbooks/templates/appwrite.env.j2`
|
||||
2. Systemd unit template → `playbooks/templates/appwrite.service.j2`
|
||||
3. Prometheus node exporter playbook → `playbooks/install_node_exporter.yml`
|
||||
4. Appwrite inventory vars → `~/Dev/inventories/bab-inventory/host_vars/bab1.mgmt.toal.ca/appwrite.yml`
|
||||
5. Monitoring inventory vars → `~/Dev/inventories/bab-inventory/host_vars/bab1.mgmt.toal.ca/monitoring.yml`
|
||||
6. HashiCorp Vault secret lookups → `~/Dev/inventories/bab-inventory/host_vars/bab1.mgmt.toal.ca/secrets.yml`
|
||||
7. `playbooks/install_appwrite.yml` — .env deploy, systemd, tags (`deps`/`image`/`configure`), restart handler, production compose URL (`appwrite.io/install/compose`)
|
||||
8. `playbooks/tasks/patch_appwrite_compose.yml` — Traefik 2.11.31 pin, image fix (appwrite-dev→official), forwardedHeaders + proxyProtocol trustedIPs for both entrypoints, handler notifications
|
||||
9. `playbooks/upgrade_appwrite.yml` — docker prune after upgrade
|
||||
10. `requirements.yml` — added `community.hashi_vault`
|
||||
11. `~/.ansible-navigator.yml` — pipelining fixed (ANSIBLE_CONFIG file was never mounted into EE; replaced with `environment-variables.set`); SSH multiplexing, fact caching, profile_tasks via CALLBACKS_ENABLED
|
||||
12. Deleted `secrets.yml.example` — contained plaintext secrets
|
||||
|
||||
---
|
||||
|
||||
## Key Numbers
|
||||
|
||||
- `appwrite_version: "1.8.1"`
|
||||
- `appwrite_traefik_version: "2.11.31"` — minimum for Docker Engine >= 29
|
||||
- `appwrite_web_port: 8080`, `appwrite_websecure_port: 8443`
|
||||
- `appwrite_traefik_trusted_ips: "192.168.0.0/22"` — HAProxy subnet; used for both `forwardedHeaders.trustedIPs` and `proxyProtocol.trustedIPs`
|
||||
- `node_exporter_version: "1.9.0"`, `node_exporter_port: 9100`
|
||||
- Vault path: `kv/data/oys/bab-appwrite` (populated 2026-03-14)
|
||||
|
||||
---
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| HashiCorp Vault for secrets | AAP + dev both need access; 1Password ansible-vault is local-only |
|
||||
| `appwrite.io/install/compose` as compose source | GitHub raw URL pointed to dev compose with `image: appwrite-dev` and broken entrypoint override |
|
||||
| Traefik pinned to 2.11.31 | Floating `traefik:2.11` tag incompatible with Docker Engine >= 29 |
|
||||
| `proxyProtocol.trustedIPs` on both Traefik entrypoints | HAProxy uses `send-proxy-v2` on both `appwrite` and `babdevapi` backends; without this Traefik returns 503 |
|
||||
| `_APP_DOMAIN_TARGET` added to .env template | Appwrite 1.8.x `console.php:49` constructs a `Domain` object from this var; null = crash |
|
||||
| systemd `Type=oneshot RemainAfterExit=yes` | `docker compose up -d` exits after starting containers; oneshot keeps unit active |
|
||||
| node exporter `security_opts: label=disable` | `:z` on `/` bind-mount would recursively relabel entire filesystem under RHEL 9 SELinux |
|
||||
| `profile_tasks` via `ANSIBLE_CALLBACKS_ENABLED` | It's an aggregate callback, not a stdout callback; `ANSIBLE_STDOUT_CALLBACK=profile_tasks` causes `'sort_order'` error |
|
||||
|
||||
---
|
||||
|
||||
## What the NEXT Session Should Do
|
||||
|
||||
1. **Run the install playbook** (skipping deps and image pull since stack is already running):
|
||||
```bash
|
||||
ansible-navigator run playbooks/install_appwrite.yml --mode stdout --skip-tags deps,image
|
||||
```
|
||||
2. **Verify** `curl -v https://appwrite.toal.ca` returns 200 (not 503)
|
||||
3. **Verify** Appwrite console loads without `Domain::__construct() null` error
|
||||
4. **Run node exporter**:
|
||||
```bash
|
||||
ansible-navigator run playbooks/install_node_exporter.yml --mode stdout
|
||||
```
|
||||
5. **Verify** `curl http://bab1.mgmt.toal.ca:9100/metrics` returns Prometheus metrics
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
None. All issues from the session are resolved.
|
||||
|
||||
---
|
||||
|
||||
## Files to Load Next Session
|
||||
|
||||
- `playbooks/install_appwrite.yml` — if continuing install/configure work
|
||||
- `playbooks/tasks/patch_appwrite_compose.yml` — if debugging compose patches
|
||||
- `docs/context/architecture.md` — for Appwrite API or EDA work
|
||||
Reference in New Issue
Block a user