secrets: ship encrypted SA key, switch install to git-clone + decrypt

- secrets/bookkeeping-sa.json.enc: team service-account key, encrypted with
  AES-256-CBC + PBKDF2(100k iter) using a 48-char random passphrase. Safe to
  commit to a public repo; the passphrase lives in the team password manager.
- scripts/decrypt-key.sh: one-liner that decrypts to ~/.config/gcp/ (mode 600)
  and prints the service-account email so users know which address to share
  their Sheet with.
- secrets/README.md: explains the crypto, decrypt flow, and rotation
  procedures (passphrase rotation vs underlying GCP key rotation).
- README + DEPLOY.md + setup.md: install flow updated. Users no longer wait
  for the admin to send a JSON; they git clone, run decrypt-key.sh with the
  passphrase from the team password manager, and continue. Cuts one
  out-of-band file transfer from the user experience.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 11:24:48 +08:00
parent d9707aeba7
commit 56003b7f69
7 changed files with 254 additions and 95 deletions

View File

@@ -16,15 +16,14 @@ You will keep your own Google Sheet (only you can read it). A small "service acc
## Before you start: a 30-second checklist
Make sure you have all five of these. If any is missing, stop and get it before continuing.
Make sure you have all four of these. If any is missing, stop and get it before continuing.
| You need | How to check |
|---|---|
| **A Google account** | You can sign in to gmail.com |
| **A Mac or Linux computer** | This guide uses Mac. Linux is identical. **Windows users**: use WSL (Windows Subsystem for Linux), or ask your AI agent for the Windows-equivalent commands |
| **OpenClaw (or another AI client) installed** | You already use it to chat with an AI |
| **A JSON file your admin sent you** | A file usually named `autoacct-sa.json` typically delivered through a password manager, encrypted email, or Slack DM. **If you don't have this, message your admin first.** |
| **A service-account email your admin gave you** | Looks like `autoacct@something.iam.gserviceaccount.com`. Your admin sent this together with the JSON. |
| **A passphrase from your admin** | A ~48-character random string, typically delivered through a password manager (1Password / Bitwarden). **If you don't have it, message your admin first.** |
| **About 5 minutes** | Don't start during a phone call. |
---
@@ -82,48 +81,57 @@ pip install google-api-python-client google-auth
---
# Part 2 — Put your admin's key file in the right place (1 minute)
# Part 2 — Unlock the bundled service-account key (1 minute)
## Step 3. Find the JSON file your admin sent you
The repo ships with the team's service-account key already encrypted (AES-256). You don't need any extra file — you just need the passphrase from your admin to unlock it.
Locate the file (e.g. `autoacct-sa.json`). It is typically in:
- Your **Downloads** folder (if downloaded from email or a link)
- A folder you exported from a password manager (1Password / Bitwarden)
- An attachment on your desktop
## Step 3. Have the passphrase ready
**Treat this file like a password.** Anyone who has it can write to any Google Sheet you've shared with your admin's service account. Don't email it to yourself, don't post it anywhere, don't put it on a USB stick.
The passphrase is a ~48-character random string. It looks something like:
## Step 4. Move the JSON file to its permanent home
Paste this into the Terminal (assuming the file is in your Downloads folder and named `autoacct-sa.json`):
```bash
mkdir -p ~/.config/gcp
mv ~/Downloads/autoacct-sa.json ~/.config/gcp/autoacct-sa.json
chmod 600 ~/.config/gcp/autoacct-sa.json
```
d03wb3gAnXyo2N8e6FYGIUTNUd3-rFu-UxuEYbVWgOOZxZnG
```
**What you should see:** nothing visible. The Terminal returns silently — that means it worked.
Your admin will have stored it in the team password manager (1Password / Bitwarden). Open that, find the entry called something like "AutoACCT decrypt passphrase", and **copy it now**. You'll paste it in the next step.
**If you see "No such file or directory":** the file isn't in `~/Downloads`. Find where it actually is, and adjust the `mv` command. For example, if it's on your Desktop:
If you can't find it, message your admin before continuing.
## Step 4. Run the decrypt script
Paste this into the Terminal:
```bash
mv ~/Desktop/autoacct-sa.json ~/.config/gcp/autoacct-sa.json
bash ~/.openclaw/workspace/skills/AutoACCT/scripts/decrypt-key.sh
```
**If the file has a different name** (e.g. `autoacct-project-12345.json`): use that name in the `mv` command, but **rename it to `autoacct-sa.json` while moving**:
The Terminal prompts:
```bash
mv ~/Downloads/autoacct-project-12345.json ~/.config/gcp/autoacct-sa.json
```
enter AES-256-CBC decryption password:
```
**Verify:** paste this into the Terminal:
**Paste the passphrase from Step 3.** (Right-click → Paste, or Cmd+V. The characters won't appear on screen — that's normal; it's hiding your password.) Press **Enter**.
**What success looks like:**
```
Decrypted to /Users/you/.config/gcp/bookkeeping-sa.json
Service-account email: bookkeeping@autoacct.iam.gserviceaccount.com
Next step: share your Google Sheet with this email (Editor).
```
**Write down the service-account email** — you'll paste it in Step 8.
**If you see `bad decrypt`:** the passphrase is wrong. Either you mistyped it, or your admin sent a different one. Try again with a fresh paste.
**Verify:**
```bash
ls -la ~/.config/gcp/
```
You should see one line containing `autoacct-sa.json` with permissions `-rw-------`.
You should see one line containing `bookkeeping-sa.json` with permissions `-rw-------`.
---
@@ -156,14 +164,14 @@ You'll paste this exact name into `config.json` in Step 10. **Tab name and confi
**Verify:** Column A says `Date`, Column N (scroll right if needed) says `Logged At`.
## Step 8. Share the sheet with your admin's service account
## Step 8. Share the sheet with the service account
This is the step that lets the skill write into your sheet.
1. Click the green **Share** button (top right of the sheet).
2. In the "Add people, groups, and calendar events" box, **paste the service-account email your admin gave you**. It looks something like:
2. In the "Add people, groups, and calendar events" box, **paste the service-account email that `decrypt-key.sh` printed back in Step 4**. It looks like:
```
autoacct@your-project-12345.iam.gserviceaccount.com
bookkeeping@autoacct.iam.gserviceaccount.com
```
3. Make sure the role on the right says **Editor** (not Viewer / Commenter).
4. **Uncheck "Notify people"** — there's no real person on the other end of that email.
@@ -212,7 +220,7 @@ Nothing visible happens — that's expected. The Terminal just made a copy of th
{
"sheet_id": "PASTE_YOUR_GOOGLE_SHEET_URL_OR_ID_HERE",
"worksheet": "Sheet1",
"service_account_path": "~/.config/gcp/autoacct-sa.json",
"service_account_path": "~/.config/gcp/bookkeeping-sa.json",
"hkd_fx_provider": "frankfurter"
}
```
@@ -229,7 +237,7 @@ Nothing visible happens — that's expected. The Terminal just made a copy of th
{
"sheet_id": "https://docs.google.com/spreadsheets/d/1abcDEF...xyz123/edit#gid=0",
"worksheet": "Sheet1",
"service_account_path": "~/.config/gcp/autoacct-sa.json",
"service_account_path": "~/.config/gcp/bookkeeping-sa.json",
"hkd_fx_provider": "frankfurter"
}
```
@@ -266,10 +274,10 @@ Switch to your Google Sheet — there's a new row with `TEST` as the merchant. *
| You see | What it means | How to fix |
|---|---|---|
| `HTTP 403` or `The caller does not have permission` | You forgot Step 8 (sharing the sheet with the service-account email), or the email was typed incorrectly | Re-share the sheet with the exact email your admin gave you, role Editor. |
| `HTTP 403` or `The caller does not have permission` | You forgot Step 8 (sharing the sheet with the service-account email), or the email was typed incorrectly | Re-run `bash scripts/decrypt-key.sh` to print the email again, then re-share the sheet with that exact address, role Editor. |
| `HTTP 400: Unable to parse range` | The `worksheet` in `config.json` doesn't match the actual tab name | Open `config.json`, fix it to exactly match the tab name at the bottom-left of your sheet (`Sheet1` or `工作表1`). |
| `HTTP 404` or `Requested entity was not found` | The `sheet_id` in `config.json` is wrong | Open your sheet in the browser, copy the **full URL** from the address bar, paste it into `sheet_id` (replacing whatever's there). |
| `FileNotFoundError ... autoacct-sa.json` | The JSON file isn't where `config.json` says it is | Run `ls -la ~/.config/gcp/` — confirm the file is there with the exact name `autoacct-sa.json`. If it has a different name, either rename it or update `service_account_path` in `config.json`. |
| `FileNotFoundError ... bookkeeping-sa.json` | The JSON file isn't where `config.json` says it is | Run `ls -la ~/.config/gcp/` — confirm the file is there with the exact name `bookkeeping-sa.json`. If it has a different name, either rename it or update `service_account_path` in `config.json`. |
| `ImportError: No module named 'googleapiclient'` | You skipped or partially failed the `pip install` step | Re-run `pip install google-api-python-client google-auth`. If that fails, try `pip3` or `python3 -m pip install ...`. |
| `config.json not found` | You skipped Step 10 | Run `cp config.example.json config.json` from inside the skill folder. |
| `Expecting value: line 1 column 1` (JSON error) | Your `config.json` has smart quotes or is otherwise malformed | Re-open with TextEdit in plain-text mode (Format → Make Plain Text), or ask your AI agent to repair it. |
@@ -284,16 +292,16 @@ If your problem is not in this table: copy the **exact error text** and paste it
|---|---|
| Use a different sheet | Repeat Steps 59 on the new sheet (including the Share-with-service-account step), then update `sheet_id` and `worksheet` in `config.json`. |
| Stop the skill writing to a sheet | Open the sheet → Share → find the service-account email → click trash icon → Save. The skill will get HTTP 403 on the next attempt. |
| Move to a new computer | Repeat Parts 1, 2, and 4 on the new computer (re-clone, copy the JSON, copy `config.json`). Your sheet and its sharing don't change. |
| You think your JSON file leaked | Message your admin immediately. They can revoke the key on the GCP side; nothing they revoke can come back to bite you afterwards. |
| Move to a new computer | Repeat Parts 1, 2, and 4 on the new computer (re-clone, re-run `decrypt-key.sh`, copy `config.json`). Your sheet and its sharing don't change. |
| You think the decrypted JSON or passphrase leaked | Message your admin immediately. They will rotate both the passphrase **and** the underlying GCP key; you'll then `git pull` + re-run `decrypt-key.sh` with the new passphrase. |
---
# Summary of what just happened (so you understand what you set up)
1. **Your computer** has the AutoACCT skill in `~/.openclaw/workspace/skills/AutoACCT/`. When you drop a receipt photo into your AI chat, the AI runs `scripts/append_row.py`, which is a small Python program.
2. **The JSON file** at `~/.config/gcp/autoacct-sa.json` is the key that authenticates the Python program as a "service account" your admin created.
2. **The repo ships an encrypted JSON file** (`secrets/bookkeeping-sa.json.enc`) — the service-account key locked with AES-256 + your team's passphrase. `decrypt-key.sh` unlocks it into `~/.config/gcp/bookkeeping-sa.json` (mode 600). The unlocked JSON authenticates the Python program as the service account your admin created in Google Cloud.
3. **The service account** is allowed to edit Google Sheets only when those sheets have been shared with its email. You shared *your* sheet with it in Step 8 — so it can only write to your sheet (and any other sheet you might share with it later).
4. **Other users on your team** have the **same JSON file** but their own sheets. The service account can write to each of their sheets only because each user has shared *their own* sheet with it. They cannot see your sheet, and you cannot see theirs.
4. **Other users on your team** decrypt the **same JSON file** with the **same passphrase** but write to **their own sheets**. The service account can write to each of their sheets only because each user has shared *their own* sheet with it. They cannot see your sheet, and you cannot see theirs — unless someone explicitly shares.
That's it. Welcome to AutoACCT.