Date: 2026-05-02 Cards: #712, #714, #718, #720 (refs #707) Author: sre-agent
Checked via heroku run --app raxx-console-prod pattern:
| Secret | Status |
|---|---|
AWS_ACCESS_KEY_ID |
ABSENT |
AWS_SECRET_ACCESS_KEY |
ABSENT |
FREESCOUT_API_KEY |
ABSENT |
POSTMARK_SERVER_API_KEY |
ABSENT |
PR #746 merged 2026-04-30. Two backup tiers are documented and configured in principle:
raxx-tickets, daily 07:00 UTC, 7-day retentionscripts/ops/freescout-backup.sh written, S3 destination s3://raxx-support-attachments/db-backups/freescout/Outstanding blocker: AWS credentials are absent from vault. The backup script on the instance cannot authenticate to S3 or call Lightsail APIs without one of:
raxx-tickets Lightsail instance (preferred — no long-lived key to rotate), ORAWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY for the raxx-freescout-backup IAM user (tracked by #744)What issue #714 still needs before it can be closed:
Confirm Tier 1 snapshot is actually running — run this from a machine with AWS credentials:
bash
aws lightsail get-instance-snapshots --region us-east-1 \
--query 'instanceSnapshots[?contains(fromInstanceName, `raxx-tickets`)].[name,state,createdAt]' \
--output table
Expected: at least one snapshot with state: available dated within the last 25 hours.
Deploy scripts/ops/freescout-backup.sh to /usr/local/bin/freescout-backup.sh on the instance once IAM credentials are available:
bash
scp -i /tmp/lightsail_us_east_1.pem \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
scripts/ops/freescout-backup.sh \
admin@54.146.13.200:/tmp/freescout-backup.sh
ssh -i /tmp/lightsail_us_east_1.pem \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
admin@54.146.13.200 \
"sudo mv /tmp/freescout-backup.sh /usr/local/bin/freescout-backup.sh && sudo chmod 750 /usr/local/bin/freescout-backup.sh"
Add root cron entry on the instance:
bash
ssh -i /tmp/lightsail_us_east_1.pem \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
admin@54.146.13.200 \
"echo '30 7 * * * root /usr/local/bin/freescout-backup.sh' | sudo tee /etc/cron.d/freescout-backup"
IAM scope required for #744 (IAM user raxx-freescout-backup):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LightsailSnapshot",
"Effect": "Allow",
"Action": [
"lightsail:CreateInstanceSnapshot",
"lightsail:GetInstanceSnapshot",
"lightsail:GetInstanceSnapshots",
"lightsail:DeleteInstanceSnapshot"
],
"Resource": "*"
},
{
"Sid": "S3BackupWrite",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:HeadObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::raxx-support-attachments",
"arn:aws:s3:::raxx-support-attachments/db-backups/freescout/*"
]
},
{
"Sid": "STSVerify",
"Effect": "Allow",
"Action": "sts:GetCallerIdentity",
"Resource": "*"
}
]
}
Once #744 lands and credentials are in Infisical at /MooseQuest/aws/freescout-backup/, wire them to the instance:
# On the raxx-tickets instance, add to /etc/environment or the cron environment:
AWS_ACCESS_KEY_ID="<value from vault>"
AWS_SECRET_ACCESS_KEY="<value from vault>"
AWS_REGION="us-east-1"
Issue #714 should remain OPEN until: Tier 1 snapshot is confirmed active AND Tier 2 cron is running on the instance.
Status: BLOCKED — blocked label is correct. The card requires the UX designer branding spec before templates are finalized. No FreeScout API token is in vault, so API automation is not currently possible.
When this unblocks: When the ux-designer branding research task completes, the implementer (feature-developer) will need SSH access to the instance and/or FreeScout admin access. Until then, the steps below are the full implementation guide.
FreeScout uses Laravel Blade templates for outbound emails. Template files live at:
/var/www/html/freescout/resources/views/emails/
Key template files (verify on instance — paths may vary by FreeScout version):
| Template | File | Sent when |
|---|---|---|
| New ticket (customer confirmation) | customer/created.blade.php or similar |
Customer submits a new ticket |
| Operator reply | customer/reply.blade.php or similar |
Operator sends a reply |
| Ticket closed | customer/closed.blade.php or similar |
Ticket is marked resolved |
FreeScout also ships a base email layout (typically layouts/email.blade.php or similar) that wraps all customer emails. Overriding the layout is the most durable approach — it applies to all email types at once.
Durability concern: Editing core template files directly will be overwritten on git pull / FreeScout update. The correct approach is to use a FreeScout theme or module. As of FreeScout 1.8.x, there is no official white-label email module for community edition. Check whether the Custom Emails module is available on the installed version. If not, use a local override approach described below.
Step 1: SSH to the instance
ssh -i /tmp/lightsail_us_east_1.pem \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
admin@54.146.13.200
Step 2: Identify the current email template structure
find /var/www/html/freescout/resources/views/emails -type f | sort
find /var/www/html/freescout/resources/views/layouts -type f | sort
Note the exact file names. FreeScout versions differ.
Step 3: Back up the original templates
sudo cp -r /var/www/html/freescout/resources/views/emails \
/var/www/html/freescout/resources/views/emails.bak-$(date -u +%Y%m%d)
Step 4: Create the branded email layout
Replace the email layout (or the header partial — check which file controls the top of every email):
Branding values to apply (Confidence Engine palette, locked 2026-04-25):
| Element | Value |
|---|---|
| Background | #f8f5f0 (snow) |
| Body text | #0f172a (ink) |
| Accent / border | #4a7c59 (moss) |
| Logo | Raxx wordmark (see logo hosting note below) |
| Footer text | Raxx Support | MooseQuest LLC. All rights reserved. |
| Footer email | support@raxx.app |
| Font | System stack: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif (no web fonts — deliverability) |
Logo hosting note (critical for email): The logo URL in email templates must be publicly accessible without authentication. Do NOT use tickets.raxx.app (CF Access protected). Options:
https://status.raxx.app/raxx-wordmark.svg) if that domain is publicly accessible.getraxx.com under a /assets/ path.The canonical wordmark SVG is at docs/design/brand/logos/raxx-wordmark-c.svg in the repo.
Step 5: Footer exact string
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 12px; color: #0f172a; margin: 0; padding: 16px 0;">
Raxx Support | MooseQuest LLC. All rights reserved.
</p>
No FreeScout attribution. No "Powered by FreeScout" or equivalent.
Step 6: Test
kris@moosequest.net.#f8f5f0.Step 7: Clear FreeScout view cache
cd /var/www/html/freescout
sudo -u www-data /usr/bin/php artisan view:clear
sudo -u www-data /usr/bin/php artisan config:cache
Step 8: Document template file locations in runbook
Update docs/ops/runbooks/freescout.md with the exact file paths modified and whether the approach is durable across FreeScout updates.
FreeScout also has a UI-based template system under Settings > Mailboxes > [mailbox name] > Auto Reply (and similar tabs). This controls text-only auto-response messages, not the full HTML email template. Check this UI after SSH template override to ensure the auto-reply text is also Raxx-branded.
Steps:
1. Log in to https://tickets.raxx.app.
2. Go to Settings (gear icon).
3. Click Mailboxes.
4. Click the mailbox name (e.g., support@raxx.app).
5. Review the Auto Reply, Satisfaction rating, and any other template tabs.
6. Replace any FreeScout branding in the text with Raxx copy.
Documented in docs/ops/runbooks/freescout.md (section: Admin account security > TOTP two-factor authentication).
This is an operator-only action. Kristerpher must scan the QR code and store recovery codes. The runbook is the deliverable for this sprint; the actual enrollment is a follow-up operator action.
Documented in docs/ops/runbooks/freescout.md (section: Branding and company settings).
Decision recorded: tickets.raxx.app/login is operator-only (behind CF Access). Minimal Raxx wordmark is appropriate. Full customer-facing branding is not required for this surface.
This is an operator action (~10 min). Click path is in the runbook.