The Car Pulse Tracker UTL (bnc-cpt-utl) is the orchestration module that ties together all components of the Car Pulse Tracker system. It provides Docker container management, Make-based build orchestration, shell actions for deployment and operations, and CI/CD integration.
DEVELOPER WORKSTATIONS GITHUB
═══════════════════════ ═══════
┌───────────────────────────────┐ ┌────────────────────────────────────┐
│ Local Laptop │ git push │ GitHub (csitea org) │
│ │────────────>│ │
│ Docker Containers: │ │ Repositories: │
│ ┌──────────┐ ┌──────────┐ │ │ ├── bnc-cpt-api (FastAPI) │
│ │ cpt-api │ │ cpt-wui │ │ │ ├── bnc-cpt-wui (Vue 3) │
│ │ :8100 │ │ :3333 │ │ │ ├── bnc-cpt-utl (Orchestration) │
│ └──────────┘ └──────────┘ │ │ ├── bnc-cpt-inf (Terraform) │
│ ┌──────────┐ ┌──────────┐ │ │ ├── bnc-cpt-cnf (Config YAML) │
│ │ the-bot │ │tesla-mock│ │ │ └── tpl-gen (Templates) │
│ │(Puppeteer)│ │ :9100 │ │ │ │
│ └──────────┘ └──────────┘ │ │ Linear Issues: team-bnc-cpt-int │
│ ┌──────────┐ ┌──────────┐ │ └───────────────────┬────────────────┘
│ │tf-runner │ │ tpl-gen │ │ │
│ │(Terraform)│ │(Jinja2) │ │ push to master
│ └──────────┘ └──────────┘ │ │
│ │ ▼
│ ~/.gcp/.bnc/key-*.json │ ┌────────────────────────────────────┐
│ ~/.slack/bot_token │ │ GitHub Actions Workers │
│ ~/.linear/api_key │ │ │
└───────────────────────────────┘ │ WUI Pipeline (ci.yaml): │
│ ┌─────────┐ ┌────────┐ ┌───────┐ │
│ │ build & │→│ deploy │→│post- │ │
│ │ test │ │to GCS │ │deploy │ │
│ └─────────┘ └────────┘ │ test │ │
│ └───────┘ │
│ API Pipeline (cd.yaml): │
│ ┌─────────┐ ┌────────┐ ┌───────┐ │
│ │ build & │→│Cloud │→│post- │ │
│ │push img │ │Run dep.│ │deploy │ │
│ └─────────┘ └────────┘ │ test │ │
│ └───────┘ │
└───────────────┬──────────────────┘
│
┌──────────────────────────────────────┘
▼
GOOGLE CLOUD PLATFORM
═════════════════════════════════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ Projects: bnc-cpt-inf │ bnc-cpt-dev │ bnc-cpt-tst │ bnc-cpt-prd │
│ │
│ ┌─────────────────────┐ ┌──────────────────────┐ ┌───────────────────┐ │
│ │ Artifact Registry │ │ Cloud Run (API) │ │ GCS Bucket (WUI) │ │
│ │ │ │ │ │ │ │
│ │ bnc-cpt-api/ │ │ {env}.api. │ │ {env}. │ │
│ │ cpt-api:latest │──│ carpulsetracker.com │ │ carpulsetracker │ │
│ │ cpt-api:{sha} │ │ :8100 │ │ .com │ │
│ └─────────────────────┘ └──────────┬───────────┘ └────────┬──────────┘ │
│ │ │ │
│ ┌─────────────────────┐ ┌──────────┴───────────┐ ┌───────┴──────────┐ │
│ │ Secret Manager │ │ Cloud DNS │ │ Cloud CDN │ │
│ │ │ │ │ │ + HTTPS LB │ │
│ │ bnc-cpt-stripe-* │ │ carpulsetracker.com │ │ + SSL Cert │ │
│ │ bnc-cpt-paypal-* │ │ CNAME → ghs.google │ │ │ │
│ │ bnc-cpt-jwt-* │ │ hosted.com │ │ │ │
│ │ bnc-cpt-admin-* │ └──────────────────────┘ └──────────────────┘ │
│ │ bnc-cpt-tesla-* │ │
│ │ bnc-cpt-stripte- │ │
│ │ test-account-* │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
EXTERNAL SERVICES
═════════════════════════════════════════════════════════════════════════════════
Payment Providers Vehicle Data Platform
───────────────── ──────────────────────
┌──────────────────┐ ┌──────────────────┐ ╔══════════════════════════════╗
│ ≋ Stripe │ │ ≋ PayPal │ ║ ◈ Tesla Fleet API ║
│ │ │ │ ║ ║
│ checkout.stripe │ │ paypal.com/ │ ║ auth.tesla.com/oauth2 ║
│ .com/pay/cs_* │ │ approve/PAY-* │ ║ fleet-api.prd.eu.vn.cloud ║
│ │ │ │ ║ .tesla.com ║
│ API: pi_*, cs_* │ │ API: 5O*, 3L* │ ║ ║
│ Webhook: whsec_* │ │ Sandbox+Live │ ║ OAuth2 → Vehicle Data ║
└──────────────────┘ └──────────────────┘ ║ → Battery Health ║
║ → Charging Stats ║
┌──────────────────┐ ┌──────────────────┐ ║ → Service History ║
│ ≋ MobilePay │ │ ≋ Google Pay │ ╚══════════════════════════════╝
│ (placeholder) │ │ (placeholder) │
│ API: mp_* │ │ │
└──────────────────┘ └──────────────────┘
Collaboration
┌──────────────────┐ ──────────────
│ ≋ Apple Pay │ ┌──────────────────┐
│ (placeholder) │ │ ✦ Slack │
└──────────────────┘ │ │
│ #bnc-cpt │
│ Bot: xoxb-... │
└──────────────────┘
┌──────────────────┐
│ ✦ Linear │
│ │
│ Team: BNC │
│ Issues: BNC-* │
└──────────────────┘
User Browser Cloud Run (API) External
──────────── ─────────────── ────────
┌────────────┐ ┌──────────────┐
│ Vue 3 SPA │──── /api/v1/ ───>│ FastAPI │
│ (CDN) │<─── JSON ───────│ :8100 │
└─────┬──────┘ └──┬──────┬────┘
│ │ │
│ Stripe redirect │ │ ╔═══════════════╗
│─────────────────────────>Stripe │ ║ Tesla Fleet ║
│<─── return_url ──────────Checkout │────────>║ API ║
│ │ │<────────║ (OAuth2+Data) ║
│ PayPal redirect │ │ ╚═══════════════╝
│─────────────────────────>PayPal │
│<─── return_url ──────────Approve │
│ │ │
│ PDF download │ │
│<─── receipt.pdf ───────────┘ │
│<─── report.pdf ──────────────────┘
UTL does not run in production. It is a development and CI/CD tool that orchestrates the other modules.
UTL manages 5 Docker containers:
| Container | Image | Purpose | Port |
|---|---|---|---|
| con-bnc-cpt-cpt-api | img-cpt-api | FastAPI backend (dev) | 8100 |
| con-bnc-cpt-wui | img-cpt-wui | Vue frontend (dev) | 3333 |
| con-bnc-cpt-tf-runner | img-cpt-tf-runner | Terraform execution | - |
| con-bnc-cpt-tpl-gen | img-cpt-tpl-gen | Template generation | - |
| con-bnc-cpt-conf-validator | img-cpt-conf-validator | Config validation | - |
| File | Containers | Purpose |
|---|---|---|
| docker-compose-api.yaml | cpt-api | API development |
| docker-compose-wui.yaml | cpt-wui | WUI development |
| docker-compose-infra.yaml | tf-runner, tpl-gen, conf-validator | Infrastructure |
Docker containers configured via src/docker/.env (local) or .env.cin (CI):
| Variable | Local | CI |
|---|---|---|
| MOUNT_WORK_DIR | /opt/bnc/bnc-cpt | /home/runner/work/.../opt/bnc/bnc-cpt |
| HOME_API_PROJ_PATH | /home/appusr/opt/bnc/bnc-cpt/bnc-cpt-api | same |
| HOME_WUI_PROJ_PATH | /home/appusr/opt/bnc/bnc-cpt/bnc-cpt-wui | same |
| API_PORT_HOST | 8100 | 8100 |
| WUI_PORT_HOST | 3333 | 3333 |
./run -a do_action_name
│
├── Parses -a flag
├── Scans src/bash/run/*.func.sh
├── Matches: do_action_name → action-name.func.sh
├── Sources the file
└── Executes the function
Naming: kebab-case.func.sh → do_snake_case function.
| Action | File | Purpose |
|---|---|---|
| do_gcp_deploy_wui | gcp-deploy-wui.func.sh | Build Vue + deploy to GCS + CDN invalidate |
| do_gcp_deploy_api_full | gcp-deploy-api-full.func.sh | Build + push + deploy API to Cloud Run |
| do_gcp_build_and_push_api_image | gcp-build-and-push-api-image.func.sh | Build + push API Docker image |
| do_gcp_deploy_cloud_run | gcp-deploy-cloud-run.func.sh | Deploy image to Cloud Run |
| do_deploy_app_to_environment | deploy-app-to-environment.func.sh | Full environment deploy |
| Action | File | Purpose |
|---|---|---|
| do_version_bump | version-bump.func.sh | Bump semver git tag (patch/minor/major) |
| Action | File | Purpose |
|---|---|---|
| do_generate_env_json | (in tpl-gen) | YAML → JSON conversion |
| do_generate_config_for_step | (in tpl-gen) | JSON → tfvars generation |
| Action | File | Purpose |
|---|---|---|
| do_sync_wui_to_remote_host | sync-wui-to-remote-host.func.sh | rsync WUI to VM |
| do_sync_api_to_remote_host | sync-api-to-remote-host.func.sh | rsync API to VM |
| do_push_all_app_repos | push-all-app-repos.func.sh | Git push all repos |
| do_pull_all_app_repos | pull-all-app-repos.func.sh | Git pull all repos |
| Category | Prefix | Example |
|---|---|---|
| Container setup | do-setup-* | make do-setup-api |
| Terraform | do-provision, do-tf-* | make do-provision ENV=dev STEP=029 |
| Testing | test-api-* | make test-api-cov |
| Config generation | do-generate-* | make do-generate-config-for-step |
| Deployment | do-gcp-* | make do-gcp-sync-secrets ENV=dev |
All terraform operations run inside con-bnc-cpt-tf-runner:
| Target | Required Vars | Purpose |
|---|---|---|
| do-provision | ENV, STEP | terraform apply |
| do-deprovision | ENV, STEP | terraform destroy |
| do-tf-plan | ENV, STEP | Plan only |
| do-tf-state-list | ENV, STEP | List state objects |
| do-tf-import | ENV, STEP, TARGET, ID | Import existing resource |
| do-tf-apply-target | ENV, STEP, TARGET | Apply single resource |
| do-tf-destroy-target | ENV, STEP, TARGET | Destroy single resource |
| do-tf-replace-target | ENV, STEP, TARGET | Destroy + recreate resource |
| do-tf-state-remove | ENV, STEP, TARGET | Remove from state |
| do-tf-state-show | ENV, STEP, TARGET | Show resource state |
| do-gcp-sync-secrets | ENV | Sync secrets from Google Sheet |
YAML (source of truth) JSON (intermediate) tfvars (output)
bnc-cpt-cnf/ bnc-cpt-cnf/ bnc-cpt-cnf/
dev.env.yaml → dev.env.json → dev/tf/029.vars.tfvars
dev/tf/029.backend-config.tfvars
tpl-gen (Jinja2) tpl-gen (Jinja2)
renders templates renders templates
Template files: bnc-cpt-cnf/src/tpl/%org%-%app%/%env%/tf/{step}.*.tfvars.tpl
UTL is used by both API and WUI CI/CD pipelines:
.env.cin → .env (Docker config for CI)MOUNT_WORK_DIR for runner pathmake do-setup-wui-no-cache builds containerdo_build_wui_vue runs type-check + builddo_gcp_deploy_wui deploys to GCSbnc-cpt-cnf/{env}.env.jsonbnc-cpt-utl/src/docker/cpt-api/Dockerfile.x86_64do_version_bump
│
├── Read latest git tag (git describe --tags)
├── Parse: v{major}.{minor}.{patch}
├── Increment (patch → minor → major at 9)
└── Create annotated git tag
┌─────────────────────────┐
│ │
WUI Build API Deploy
│ │
VITE_APP_VERSION=v0.2.3 APP_VERSION=v0.2.2
│ │
<meta name="version" Cloud Run env var
content="v0.2.3" /> → /health, /, /api
WUI: git tag → gcp-deploy-wui.func.sh reads tag → passes VITE_APP_VERSION to Docker build → Vite injects <meta> tag → visible in HTML source
API: git tag → cd.yaml reads tag → passes APP_VERSION to Cloud Run → Pydantic settings loads it → visible at API endpoints
| Environment | Purpose | Auto-deploy | Manual deploy |
|---|---|---|---|
| inf | Infrastructure testing | On master push | workflow_dispatch |
| dev | Development | On master push | workflow_dispatch |
| tst | Testing/QA | - | workflow_dispatch only |
| prd | Production | - | workflow_dispatch only |
All operations authenticate via service account keys at ~/.gcp/.bnc/:
| Key File | GCP Project | Used By |
|---|---|---|
| key-bnc-cpt-inf.json | bnc-cpt-inf | Terraform, deploy |
| key-bnc-cpt-dev.json | bnc-cpt-dev | Terraform, deploy |
| key-bnc-cpt-tst.json | bnc-cpt-tst | Terraform, deploy |
| key-bnc-cpt-prd.json | bnc-cpt-prd | Terraform, deploy |
| key-bnc-cpt-all.json | Cross-project | Google Sheets, shared ops |
UTL provides shell actions for posting messages and threads to Slack channels from the CLI.
chat:write and chat:write.publicim:write for direct messagesxoxb-...)./run -a do_slack_setup and paste the tokenThe token is stored at ~/.slack/bot_token (chmod 600).
| Action | File | Purpose |
|---|---|---|
| do_slack_setup | slack-setup.func.sh | Save bot token to ~/.slack/bot_token |
| do_slack_post_msg | slack-post-msg.func.sh | Post message to a channel |
| do_slack_post_thread | slack-post-thread.func.sh | Post title + threaded reply |
# Simple message
CHANNEL=bnc-cpt MSG="Hello" ./run -a do_slack_post_msg
# Post file contents (markdown auto-converted to Slack format)
CHANNEL=bnc-cpt FILE=doc/md/cle-ad-hoc.md ./run -a do_slack_post_msg
# Thread: title + file as reply
CHANNEL=bnc-cpt TITLE="Update" FILE=doc/md/cle-ad-hoc.md ./run -a do_slack_post_thread
# Reply in existing thread
CHANNEL=bnc-cpt THREAD_TS=1770813763.163469 MSG="reply" ./run -a do_slack_post_msg
# Mention a user (use their Slack Member ID)
CHANNEL=bnc-cpt MSG="<@U079LU2D17G> check this" ./run -a do_slack_post_msg
When posting .md files, content is automatically converted to Slack mrkdwn format:
- Markdown tables → monospace code blocks
- **bold** → *bold*
- # Headers → *Headers*
- [text](url) → <url|text>
| Variable | Used By | Description |
|---|---|---|
| CHANNEL | post_msg, post_thread | Channel name without # |
| MSG | post_msg | Message text (Slack mrkdwn) |
| FILE | post_msg, post_thread | File path (overrides MSG/REPLY) |
| THREAD_TS | post_msg | Parent message timestamp for thread reply |
| TITLE | post_thread | Main message text |
| REPLY | post_thread | Text reply (used if FILE not set) |
WUI Pipeline (ci.yaml)
══════════════════════════════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────────────────────────┐
│ Job 1: build-and-test (all pushes) │
│ │
│ clone repos ──> docker build ──> type-check ──> wait for ──> Puppeteer │
│ (gh api) (Dockerfile) (do_build_ Vite dev npm test │
│ wui_vue) server (lcl) │
│ │
│ Output: Mochawesome HTML report artifact │
└─────────────────────────────────────┬───────────────────────────────────┘
│ (master only)
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Job 2: prepare-deploy │
│ │
│ push to master ──> environments = ["inf", "dev"] │
│ workflow_dispatch ──> environments = ["<selected>"] │
└─────────────────────────────────────┬───────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────────────┐
│ Job 3: deploy (matrix: inf, dev, tst) │
│ │
│ clone ──> git tag ──> load cnf ──> docker ──> GCP ──> Vue build │
│ repos version JSON build auth (VITE_APP_ │
│ VERSION) │
│ ──> gsutil sync ──> CDN invalidate ──> health check │
│ to GCS bucket https://{fqdn} │
└─────────────────────────────────────┬──────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────────────┐
│ Job 4: post-deploy-test (matrix: inf, dev, tst — never prd) │
│ │
│ clone ──> load cnf ──> GCP auth ──> fetch secrets ──> docker build │
│ repos JSON (Secret Mgr) the-bot │
│ STRIPTE_* │
│ PAYPAL_* │
│ │
│ ──> wait for WUI ──> docker exec npm test ──> upload report │
│ health check (ENV={env}, creds via -e) Mochawesome │
└────────────────────────────────────────────────────────────────────────┘
API Pipeline (cd.yaml)
══════════════════════════════════════════════════════════════════════════════
┌────────────────────────────────────────────────────────────────────────┐
│ Job 1: prepare │
│ │
│ push to master ──> environments = ["inf", "dev"] │
│ workflow_dispatch ──> environments = ["<selected>"] │
└─────────────────────────────────────┬──────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────────────┐
│ Job 2: deploy (matrix: inf, dev, tst) │
│ │
│ clone ──> git tag ──> load cnf ──> GCP auth ──> docker build │
│ repos version JSON (target=production) │
│ │
│ ──> push to ──> gcloud run ──> domain ──> health check │
│ Artifact deploy mapping /health (200) │
│ Registry (secrets (CNAME) /docs (200) │
│ from SM) │
└─────────────────────────────────────┬──────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────────────┐
│ Job 3: post-deploy-test (matrix: inf, dev, tst — never prd) │
│ │
│ clone ──> load cnf ──> GCP auth ──> fetch secrets ──> curl smoke │
│ repos JSON (Secret Mgr) tests │
│ │
│ Smoke tests: ──> pip install ──> pytest test_post_deploy.py │
│ GET /health (200) httpx (API_URL={url}) │
│ GET /docs (200) │
│ POST /create-intent (<500) │
└────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
│ YAML Config │───>│ tpl-gen │───>│ tfvars │───>│ tf-runner│
│ (bnc-cpt- │ │ Jinja2 │ │ (bnc-cpt- │ │ │
│ cnf) │ │ rendering │ │ cnf/{env}/ │ │ terraform│
│ │ │ │ │ tf/) │ │ apply │
│ dev.env.yaml │ │ dev.env.json │ │ 029.vars. │ │ │
│ │ │ │ │ tfvars │ │ │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────┘
│
┌────────────────────────┘
▼
┌────────────────────────┐
│ GCP Resources Created │
│ ├── Secret Manager │
│ ├── Artifact Registry │
│ ├── Cloud Run │
│ ├── Cloud DNS │
│ ├── GCS Buckets │
│ └── IAM Policies │
└────────────────────────┘
Browser (Vue SPA) Cloud Run (API) External
───────────────── ─────────────── ────────
① Select Plan
(Basic €19 / Pro €29 / Bulk €149)
│
② Select Payment Method
(Stripe / PayPal / GooglePay / ApplePay)
│
③ Agree to Terms
│
④ Click Pay
│
├── POST /api/v1/payment/create-intent ───────>┐
│ {plan, amount, payment_method} │
│ ▼
│ ┌──────────────────┐
│ Stripe │ stripe.checkout │
│ │ .Session.create()│
│ └────────┬─────────┘
│ │
│<── {redirectUrl, paymentIntentId} ───────────┘
│
⑤ Redirect to provider
│
│────────────────────> ┌──────────────────┐
│ │ checkout.stripe │
│ Fill test card: │ .com/pay/cs_* │
│ 4242 4242 4242 4242│ │
│ Click Pay │ OR │
│ │ │
│ │ paypal.com/ │
│ │ approve/PAY-* │
│<── return_url ──────└──────────────────┘
│
⑥ Verify payment
│
├── POST /api/v1/payment/verify ──────────────>┐
│ {payment_id} │
│ ▼
│ ┌──────────────────┐
│ │ stripe.checkout │
│ │ .Session.retrieve│
│ │ payment_status │
│ │ == "paid" │
│ └────────┬─────────┘
│<── {status: "succeeded", receipt_url} ───────┘
│
⑦ Download receipt
│
├── GET /api/v1/payment/receipt/{id}/pdf ─────>┐
│ │ WeasyPrint
│<── receipt.pdf (HTML→PDF) ───────────────────┘
Browser (Vue SPA) Cloud Run (API) Tesla Fleet API
───────────────── ─────────────── ═══════════════
① Initiate OAuth
│
├── POST /api/v1/tesla/oauth/initiate ────────>┐
│ {paymentId, plan} │ generate state token
│ │ store in _states[state]
│ NOTE: paymentId and plan are CPT-internal │ (paymentId + plan are
│ metadata — NOT sent to Tesla. They link │ stored as state metadata
│ the OAuth session back to the payment │ for future use: audit,
│ that authorized this report request. │ linking reports to payments)
│<── {authUrl, state} ─────────────────────────┘
│
② Redirect to Tesla Login
│
│─────────────────────> ╔══════════════════════╗
│ User authenticates ║ auth.tesla.com ║
│ with Tesla account ║ /oauth2/v3/authorize ║
│ and grants consent ║ ?client_id=... ║
│ ║ &state=... ║
│ ╚══════════╤═══════════╝
│ │
│ │ Tesla redirects to
│ │ TESLA_REDIRECT_URI
│ ▼
③ Callback: validate + exchange + fetch + store (all server-side)
│
│ GET /api/v1/tesla/oauth/callback
│ ?code=...&state=...&issuer=...
│ │
│ ├── validate state (CSRF)
│ │ pop from _states, check age < 10min
│ │
│ ├── exchange code ──────> ╔═════════════════╗
│ │ for access_token ║ auth.tesla.com ║
│ │ (LOCAL variable, ║ /oauth2/v3/ ║
│ │ never stored) ║ token ║
│ │<── access_token ─────── ╚═════════════════╝
│ │
│ ├── fetch vehicles ─────> ╔═════════════════╗
│ │ with user's token ║ fleet-api/ ║
│ │<── vehicle list ─────── ║ api/1/vehicles ║
│ │ ╚═════════════════╝
│ │
│ ├── build report ───────> ╔═════════════════╗
│ │ (5 parallel calls) ║ fleet-api/ ║
│ │ • vehicle_data ║ vehicle_data ║
│ │ • charging_history ║ charging/hist ║
│ │ • recent_alerts ║ recent_alerts ║
│ │ • service_data ║ service_data ║
│ │ • warranty_details ║ warranty/det ║
│ │<── all data ────────── ╚═════════════════╝
│ │
│ ├── store report in _reports[session_id]
│ │ (token goes out of scope — discarded)
│ │
│ └── redirect ─────────────────────────────┐
│ │
│<── 302 Redirect: {frontend}?oauth=success&session_id=XXX ──────┘
│
④ WUI reads URL params
│
│ App.vue onMounted:
│ • reads oauth=success, session_id from URL
│ • store.setSessionId(session_id)
│ • store.setStep('verify')
│ • cleans URL with history.replaceState
│
⑤ Fetch pre-built report
│
├── GET /api/v1/tesla/report/{session_id} ────>┐
│ │ pop from _reports
│ │ (one-time retrieval)
│<── {vehicle, vehicleData, chargingHistory, │
│ recentAlerts, serviceData, │
│ warrantyDetails} ─────────────────────────┘
│
⑥ Display report dashboard
│
│ store.setVehicleReport(report)
│ store.setStep('report')
│
│ (Optional) Download PDF:
├── POST /api/v1/tesla/vehicle-report/pdf ────>┐
│ {vehicleReport} │ WeasyPrint
│<── report.pdf ───────────────────────────────┘
KEY DESIGN DECISIONS:
• Token is a LOCAL VARIABLE in fetch_and_store_report() — never stored globally
• Each user gets a unique session_id — no cross-user interference
• Reports auto-expire after 15 minutes (_cleanup_expired)
• One-time retrieval: report is deleted after GET (pop from _reports)
• App credentials (client_id + secret) != user authorization (per-user token)
Source of Truth Distribution Consumption
────────────── ──────────── ───────────
┌──────────────────┐
│ GCP Secret Mgr │
│ (per-project) │
│ │
│ bnc-cpt-{name} │
└────────┬─────────┘
│
├──────── Cloud Run ──────────────> API container
│ (--set-secrets) env vars at deploy time
│
├──────── CI/CD Worker ───────────> post-deploy-test jobs
│ (gcloud secrets docker exec -e (in-memory)
│ versions access) masked in CI logs
│
└──────── Local Dev ─────────────> Docker containers
eval "$(ENV=dev bash docker-compose env passthrough
gcp-fetch-secrets.sh)" (in-memory, never on disk)
Naming Convention:
┌──────────────────────────────┬───────────────────────────┐
│ GCP Secret ID │ Env Var Name │
├──────────────────────────────┼───────────────────────────┤
│ bnc-cpt-stripe-secret-key │ STRIPE_SECRET_KEY │
│ bnc-cpt-jwt-secret-key │ JWT_SECRET_KEY │
│ bnc-cpt-admin-password-hash │ ADMIN_PASSWORD_HASH │
│ bnc-cpt-stripte-test-* │ STRIPTE_TEST_ACCOUNT_* │
└──────────────────────────────┴───────────────────────────┘
Rule: strip "bnc-cpt-" prefix → replace "-" with "_" → UPPERCASE
All service credentials are stored under the $HOME directory of the OS user who invoked the shell (e.g. /home/ysg for Yordan, /Users/petri for Petri). The convention is $HOME/.<service-name>/ for simple tokens and $HOME/.gcp/.<org>/ or $HOME/.ssh/.<org>/ for multi-file credentials.
| Item | Path |
|---|---|
| API key | $HOME/.linear/api_key |
Single-line personal API key. Used by do_linear_comment_issue, do_linear_close_issue, and other Linear shell actions.
| Item | Path |
|---|---|
| Bot token | $HOME/.slack/bot_token |
Bot User OAuth Token (xoxb-...). Created via ./run -a do_slack_setup. Used by do_slack_post_msg and do_slack_post_thread.
| Item | Path |
|---|---|
| Personal access token | $HOME/.github/token |
Used by gh CLI and shell actions that interact with GitHub API.
All GCP service account key files are under $HOME/.gcp/.<org>/ (e.g. $HOME/.gcp/.bnc/):
| File | GCP Project | Purpose |
|---|---|---|
$HOME/.gcp/.bnc/key-bnc-cpt-inf.json |
bnc-cpt-inf | Infrastructure project |
$HOME/.gcp/.bnc/key-bnc-cpt-dev.json |
bnc-cpt-dev | Development environment |
$HOME/.gcp/.bnc/key-bnc-cpt-tst.json |
bnc-cpt-tst | Test environment |
$HOME/.gcp/.bnc/key-bnc-cpt-prd.json |
bnc-cpt-prd | Production environment |
$HOME/.gcp/.bnc/key-bnc-cpt-all.json |
Cross-project | Google Sheets, shared ops |
Used by gcloud auth activate-service-account, gsutil, Terraform (via tf-runner), deployment shell actions, and the gsheet_secrets_to_gcp.py script.
All SSH and encryption keys are under $HOME/.ssh/.<org>/ (e.g. $HOME/.ssh/.bnc/):
| File | Purpose |
|---|---|
$HOME/.ssh/.bnc/id_rsa.sys+bnc-cpt-crs@carpulsetracker.com |
SSH key for git operations |
$HOME/.ssh/.bnc/bnc-cpt-crs.pem |
KeePassXC database key |
$HOME/.ssh/.bnc/tesla-private-key.pem |
Tesla Fleet API private key |
$HOME/.ssh/.bnc/tesla-public-key.pem |
Tesla Fleet API public key (served at /.well-known/appspecific/com.tesla.3p.public-key.pem) |
All credential paths in code use $HOME (not hardcoded usernames) and derive the org from $ORG or $ORG_APP:
- GCP keys: $HOME/.gcp/.${ORG}/key-${ORG_APP}-${ENV}.json
- SSH keys: $HOME/.ssh/.${ORG}/
- KeePassXC: $HOME/.ssh/.${ORG}/${ORG_APP}-crs.pem
| Module | Relationship |
|---|---|
| bnc-cpt-api | UTL builds, tests, and deploys the API |
| bnc-cpt-wui | UTL builds and deploys the WUI |
| bnc-cpt-inf | UTL executes Terraform via tf-runner container |
| bnc-cpt-cnf | UTL generates config via tpl-gen container |
| tpl-gen | UTL hosts the template generator container |