# Sheet Relay A local Node.js service that exposes selected Google Sheets tabs as bearer-token-protected JSON endpoints. ## Run with Docker ```powershell docker login git.lzstealth.com docker compose pull docker compose up -d docker compose logs -f ``` Open `http://localhost:3000`. The service is bound to localhost by default and pulls `git.lzstealth.com/lzstealth/google-sheets-to-api:latest`. Its configuration is persisted in `data/config.json` beside the Compose file, so it survives image updates and container replacements. Pushes to `main` publish both the immutable commit tag and `latest` through Gitea Actions. The workflow uses the global `PACKAGE_REGISTRY_USERNAME` and `PACKAGE_REGISTRY_PASSWORD` secrets; grant that account permission to publish packages for the `lzstealth` organization. Stop it with: ```powershell docker compose down ``` ## Reverse proxy deployment The Compose service listens only at `127.0.0.1:3000`. Put your existing reverse proxy in front of it for HTTPS, public routing, access logs, and any network policy. Configure that proxy's upstream as `http://127.0.0.1:3000` and forward the original `Host`, client address, and scheme headers. Check container health and logs with: ```powershell docker compose ps docker compose logs -f sheet-relay ``` ## Run with Node ```powershell npm install npm start ``` Open `http://localhost:3000`. The first launch creates `data/config.json` and prints a one-time admin token. Keep this token private; use it to unlock the configuration console. ## Configure 1. Choose **API key** for public/readable sheets, or **Service account** for private sheets. 2. With a service account, share each spreadsheet with the `client_email` in its JSON credential as a Viewer. 3. Keep the default 45 requests per minute to leave a buffer below a 50 RPM allowance, or set the quota that applies to your Google project. 4. Load a spreadsheet ID, check the tabs to expose, then publish the endpoint. Sheet Relay generates its bearer token automatically; copy it from the endpoint controls. ## Consume ```powershell curl http://localhost:3000/api/v1/YOUR_PUBLICATION_ID -H "Authorization: Bearer YOUR_ENDPOINT_TOKEN" ``` Only the selected tabs are returned. Responses use a configurable short cache, reducing Google API calls and respecting the shared rate limiter. Each endpoint can return either raw sheet rows or objects keyed from the first row's headers. Object output requires non-empty, unique headers. It can also limit reads with an A1 range such as `A1:H500`, cap rows per sheet, and omit blank rows. Editing any of these options clears that endpoint's cached response immediately. Google `429` and `5xx` responses retry up to two times. Retries honor Google's `Retry-After` header when present, otherwise use 1-second then 2-second backoff, with every retry passing through the same configured credential rate limiter. ## Operations The **Operations** page provides service uptime, endpoint health, endpoint and tab-cache counts, and the latest 50 API request outcomes. It retains 500 entries, supports endpoint/status/result filters, and can clear the retained log. Request logs record the endpoint ID, status, cache/stale state, duration, and client address; they never store bearer tokens. Use **Refresh** to update an endpoint cache immediately, **Disable** to return `503` without deleting its setup, or **Rotate token** to generate and copy a replacement token. Rotation invalidates the previous token immediately. Background cache refresh can be enabled per endpoint. Use **Export redacted config** to create a transferable configuration without Google secrets, endpoint tokens, or the admin token. Importing replaces the current configuration; credentials have no secrets, endpoints receive new tokens, and endpoints remain disabled until you complete credential setup and explicitly enable them. The public health check is available at: ```text GET /health ``` Prometheus-compatible operational counters are available at `GET /metrics`. This endpoint requires the `x-admin-token` header. Browser-origin requests are denied by default. Use `*` in the **Operations** page to allow all origins, or enter exact origins such as `https://app.example.com`, one per line, to allow selected browser clients. Server-to-server API clients are unaffected because they do not send an `Origin` header. When the service is behind a reverse proxy, set **Trusted reverse proxy hops** to the number of proxies directly in front of it, typically `1`. This allows the request audit log to use the original client address from forwarded headers. Leave it at `0` unless the proxy path is trusted. ## Security notes `data/config.json` contains Google credentials and endpoint tokens. It is intentionally ignored by Git; protect the machine and back up the file securely. Put this service behind HTTPS when it is reachable beyond localhost.