14 KiB
Firebase Admin: administrator command sequence
Prepared only: none of the privileged commands below has been executed by the agent. No rebuild is needed. Do not paste passwords, keys, credential JSON, tokens, HAR files, or “Copy as cURL” output into chat. Never enable shell tracing (set -x) during this procedure.
This sequence is deliberately scoped to the known credential /etc/motorsignal/firebase-admin.json, website user motorsignal, and deployed revision 8b3a7aa29290b58a9456a564785e9c73cd8e3093. It stops rather than overwriting configuration if the known baseline has changed. If a different credential or identity is required, stop for a reviewed provisioning plan; do not create a new key or loosen file permissions.
0. Transfer the non-secret probe from your Windows computer
Run in local PowerShell, from C:\Users\Bill\Desktop\flutter-app. This only uploads diagnostic source; it does not contain credentials or change services.
scp -i C:/Users/Bill/.ssh/motorsignal_prod output/deployment/20261004-firebase-admin/admin/probe-firebase-admin.mjs motorsignal-admin@89.31.239.10:/home/motorsignal-admin/probe-firebase-admin.mjs
ssh -i C:/Users/Bill/.ssh/motorsignal_prod motorsignal-admin@89.31.239.10
Review the uploaded file, then use your existing secure administrator access on this VPS. sudo authentication stays in your own terminal; never send it to the agent. Commands below use ordinary SSH plus interactive sudo, not a new root SSH login or new access grant.
1. Read-only validation; no configuration edits or restart
Run in your VPS SSH shell:
set +x
systemctl show motorsignal-website.service -p User -p ActiveState -p EnvironmentFiles -p DropInPaths
sudo stat -c '%U %G %a %n' /etc/motorsignal/firebase-admin.json
sudo -u motorsignal env -i PATH=/usr/bin:/bin /usr/bin/node --input-type=module - metadata < /home/motorsignal-admin/probe-firebase-admin.mjs
The probe reads the existing file as the website user. It prints only type, project ID, service-account email, mode, and readability. It requires a regular file owned by that user, no world permissions, and no group write/execute permission. It requires project motoapp-d22e0. Failure means STOP; do not chmod the shared credential to bypass it.
Identity-owner approval and IAM review: separate gates
In your secure Google Cloud Console administrator session, select motoapp-d22e0, locate the exact service-account email printed above, and verify that the account is enabled. Review its existing project/database/inherited grants and conditions. Confirm effective datastore.entities.get and datastore.entities.list on the production database. Inspect any unexpectedly broad grants with the owner; do not automatically reuse an unrelated identity merely because it can read data.
Obtain explicit approval from the identity owner for website runtime reuse of this exact identity. Record the approver, identity email, purpose, scope, and approval reference in your normal secure operations record. IAM access and a routing-service reference do not themselves constitute reuse approval. No command below grants IAM roles, changes rules, or creates a key.
Firestore server SDK reads use IAM, not the website client's security rules. roles/datastore.viewer includes document get/list, but it permits reads across Firestore resources; it is a reference for reviewing existing access, not a command to broaden grants. Google Firestore IAM reference
After owner approval, run in your VPS SSH shell. Enter only the non-secret email and approval reference:
read -r -p 'Approved service-account email: ' APPROVED_IDENTITY
read -r -p 'Identity-owner approval reference: ' APPROVAL_REFERENCE
test -n "$APPROVED_IDENTITY" && test -n "$APPROVAL_REFERENCE"
sudo -u motorsignal env -i PATH=/usr/bin:/bin /usr/bin/node --input-type=module - probe "$APPROVED_IDENTITY" < /home/motorsignal-admin/probe-firebase-admin.mjs
Require firestoreReadProbes: PASS. The probe exercises ADC using the deployed website's installed Firebase Admin SDK: the actual public/scheduled-or-assembling community query shape, limited to one result, plus two document reads on the exact block/participant path shapes. The latter use the fixed name website-credential-read-probe; missing documents are acceptable and no document is created. No document contents or returned IDs are printed, and no collection-group scans are performed. These probes check get/list access; they do not prove every route authorization branch or establish human approval. A permission, disabled/invalid credential, missing-index, ownership, or timeout error means STOP. Review privately; do not create indexes, fixtures, or keys to force success in this task.
The probe uses clean process settings and checks file readability, but the post-restart browser check below remains required to establish access from the actual website process. Firebase ADC setup
2. Configuration changes: only after both gates pass
Run this block in your VPS SSH shell using your secure interactive sudo access. It rechecks approval and Firestore reads, preserves the baseline, creates only a website-specific environment file/drop-in, and restarts only the website. It does not touch the shared credential, routing environment, backend environment, code, or build assets.
The fixed backup path is /var/backups/motorsignal-website/firebase-admin-20261004. If it already exists, the block stops: do not reuse or overwrite it. If any failure occurs after the backup is created, use section 4 instead of blindly rerunning.
sudo /bin/bash -s -- "$APPROVED_IDENTITY" "$APPROVAL_REFERENCE" <<'ADMIN'
set -euo pipefail
set +x
umask 077
test "$#" -eq 2 && test -n "$1" && test -n "$2"
read -r -p 'Owner approved this exact identity for website reuse? Type APPROVED: ' ANSWER </dev/tty
test "$ANSWER" = APPROVED
UNIT=/etc/systemd/system/motorsignal-website.service
ENV_FILE=/etc/motorsignal/website.env
DROP_DIR=/etc/systemd/system/motorsignal-website.service.d
DROP_FILE="$DROP_DIR/10-firebase-admin.conf"
BACKUP=/var/backups/motorsignal-website/firebase-admin-20261004
# Refuse drift, symlinks, existing target files, or competing runtime configuration.
test ! -L "$UNIT" && test ! -L /etc/motorsignal
test "$(sha256sum "$UNIT" | cut -d ' ' -f1)" = 68ed666be77f15f0d9eb9bbfeffff7499c0e0c9618a42e57243ac554aa76d182
test -z "$(systemctl show motorsignal-website.service -p DropInPaths --value)"
test -z "$(systemctl show motorsignal-website.service -p EnvironmentFiles --value)"
test ! -e "$ENV_FILE" && test ! -L "$ENV_FILE"
test ! -e "$DROP_DIR" && test ! -L "$DROP_DIR"
test -z "$(find /opt/motorsignal/current/website -maxdepth 1 -name '.env*' -print -quit)"
systemctl show motorsignal-website.service -p Environment --value | /usr/bin/python3 -c 'import shlex,sys; e=dict(s.split("=",1) for s in shlex.split(sys.stdin.read()) if "=" in s); assert set(e)=={"NODE_ENV","HOSTNAME","PORT","MOTORSIGNAL_API_BASE_URL"}; assert e["NODE_ENV"]=="production" and e["PORT"]=="3001"'
curl --fail --silent --show-error --max-time 15 https://motorsignal.app/api/version | /usr/bin/python3 -c 'import json,sys; assert json.load(sys.stdin)["revision"]=="8b3a7aa29290b58a9456a564785e9c73cd8e3093"'
sudo -u motorsignal env -i PATH=/usr/bin:/bin /usr/bin/node --input-type=module - probe "$1" < /home/motorsignal-admin/probe-firebase-admin.mjs
# All validation above must succeed before configuration or backup writes.
if test -e /var/backups/motorsignal-website; then
test ! -L /var/backups/motorsignal-website
test "$(stat -c '%u %a' /var/backups/motorsignal-website)" = '0 700'
else
mkdir -m 0700 /var/backups/motorsignal-website
fi
mkdir -m 0700 "$BACKUP"
cp --preserve=all "$UNIT" "$BACKUP/unit.before"
printf '%s\n' 'website.env absent' '10-firebase-admin.conf absent' 'drop-in directory absent' > "$BACKUP/prior-state.txt"
printf 'identity=%s\napproval_reference=%s\n' "$1" "$2" > "$BACKUP/approval.txt"
printf '%s\n' 'FIREBASE_PROJECT_ID=motoapp-d22e0' 'GOOGLE_APPLICATION_CREDENTIALS=/etc/motorsignal/firebase-admin.json' > "$BACKUP/website.env.applied"
printf '%s\n' '[Service]' 'EnvironmentFile=/etc/motorsignal/website.env' > "$BACKUP/10-firebase-admin.conf.applied"
date -u '+%Y-%m-%d %H:%M:%S UTC' > "$BACKUP/restart-since.txt"
printf 'Protected backup: %s\nRollback: section 4 of the administrator runbook\n' "$BACKUP"
install -m 0640 -o root -g motorsignal "$BACKUP/website.env.applied" "$ENV_FILE"
install -d -m 0755 -o root -g root "$DROP_DIR"
install -m 0644 -o root -g root "$BACKUP/10-firebase-admin.conf.applied" "$DROP_FILE"
sudo -u motorsignal test -r "$ENV_FILE"
sudo -u motorsignal test -r /etc/motorsignal/firebase-admin.json
systemctl daemon-reload
systemctl restart motorsignal-website.service
systemctl is-active --quiet motorsignal-website.service
printf '%s\n' 'Website restarted. Authenticated browser and post-restart log checks are still required.'
ADMIN
Do not run concurrently with another administrator editing website configuration. The prior unit is backed up but never rewritten. The added environment file contains only project/path settings; credential JSON remains in its existing protected file. No Firebase credentials belong in .next, public, next.config, NEXT_PUBLIC_*, shell history, or browser assets.
3. Read-only post-restart verification
First run in your VPS SSH shell (no sudo required for HTTP checks):
/usr/bin/node --input-type=module - <<'HTTP'
const expected = '8b3a7aa29290b58a9456a564785e9c73cd8e3093';
const version = await fetch('https://motorsignal.app/api/version', { cache:'no-store', signal:AbortSignal.timeout(15000) });
if (version.status !== 200 || (await version.json()).revision !== expected) throw new Error('revision_check_failed');
const response = await fetch('https://motorsignal.app/api/group-rides', { cache:'no-store', signal:AbortSignal.timeout(15000) });
if (response.status !== 401 || (await response.json()).error !== 'unauthorized') throw new Error('unauthenticated_401_failed');
console.log('PASS: unchanged revision; unauthenticated discovery 401');
HTTP
Then, in your existing authorized production browser session, open https://motorsignal.app/group-rides/manage and select Explore Community Rides (or Retry Loading). In that browser's Network panel, inspect the actual GET /api/group-rides response: require HTTP 200 and JSON with rides as an array and nextCursor as null or a string. Keep request headers/tokens in the browser; do not export them or copy them into a shell. This is the authenticated end-to-end check; the service-account probe is not a substitute.
If the actual eligible result is empty, require No Community Rides Scheduled, not Community rides could not be loaded. If results are populated, verify that view and record the natural empty-result check as not exercised; do not create/delete rides or mock a production response. If pagination remains, do not claim the entire candidate set is empty from only the first page. A 401, 403, 500, or malformed response means the task is not verified.
After that authenticated request, run in your VPS SSH shell:
sudo /bin/bash -s <<'LOGS'
set -euo pipefail
set +x
SINCE=$(cat /var/backups/motorsignal-website/firebase-admin-20261004/restart-since.txt)
systemctl is-active --quiet motorsignal-website.service
journalctl -u motorsignal-website.service --since "$SINCE" --no-pager | /usr/bin/python3 -c 'import re,sys; s=sys.stdin.read(); assert s.strip() and "-- No entries --" not in s; c=len(re.findall(r"Could not load the default credentials|Failed to parse FIREBASE_SERVICE_ACCOUNT|\[FIREBASE_ADMIN\].*Failed|\[GROUP_RIDE_DISCOVERY\] Failed",s)); print("Post-restart credential/discovery failures:",c); sys.exit(1 if c else 0)'
LOGS
This prints a count, not arbitrary log lines. Zero failures is meaningful only alongside the successful authenticated request. Review any failure privately with administrator access; do not paste raw secret-bearing logs into chat. No Firestore rules or entitlements should be weakened.
4. Exact configuration rollback (website only)
Run in your VPS SSH shell using secure interactive sudo access. This also handles partial application. It refuses to overwrite subsequent edits, moves only the two files created above into the protected backup, leaves the shared credential untouched, and restarts only the website. The new empty drop-in directory is intentionally retained; it has no active configuration. No code rollback/rebuild is needed.
sudo /bin/bash -s <<'ROLLBACK'
set -euo pipefail
set +x
umask 077
BACKUP=/var/backups/motorsignal-website/firebase-admin-20261004
UNIT=/etc/systemd/system/motorsignal-website.service
ENV_FILE=/etc/motorsignal/website.env
DROP_FILE=/etc/systemd/system/motorsignal-website.service.d/10-firebase-admin.conf
test -d "$BACKUP" && test ! -L "$BACKUP"
test "$(stat -c '%u %a' "$BACKUP")" = '0 700'
test ! -L "$UNIT"
test ! -L /etc/motorsignal && test ! -L /etc/systemd/system/motorsignal-website.service.d
cmp --silent "$UNIT" "$BACKUP/unit.before"
for NAME in website.env 10-firebase-admin.conf; do
if test "$NAME" = website.env; then TARGET="$ENV_FILE"; else TARGET="$DROP_FILE"; fi
test ! -L "$TARGET"
if test -e "$TARGET"; then
cmp --silent "$TARGET" "$BACKUP/$NAME.applied"
test ! -e "$BACKUP/$NAME.removed" && test ! -L "$BACKUP/$NAME.removed"
fi
done
if test -e "$DROP_FILE"; then mv -- "$DROP_FILE" "$BACKUP/10-firebase-admin.conf.removed"; fi
if test -e "$ENV_FILE"; then mv -- "$ENV_FILE" "$BACKUP/website.env.removed"; fi
systemctl daemon-reload
systemctl restart motorsignal-website.service
systemctl is-active --quiet motorsignal-website.service
printf '%s\n' 'Prior website configuration restored; shared credential and deployed code unchanged.'
ROLLBACK
Repeat the revision/unauthenticated HTTP checks after rollback. The prior missing-ADC failure is expected to return; rollback restores configuration, not a working credential setup. Retain the protected backup and approval record. No password, key, token, or credential content is needed in chat.