02-Architecture / 02.06.Proxy-Proposal

02.06.Proxy Proposal

02.06. Proxy Proposal

Project: Car Pulse Tracker (bnc-cpt) Date: 2026-02-16 Status: PROPOSAL Affects: bnc-cpt-api, bnc-cpt-utl, bnc-cpt-cnf


1. Problem Statement

1.1 The Core Challenge

The Tesla Fleet API requires OAuth callback URLs registered in the Tesla Developer Portal. Currently only https://dev.api.carpulsetracker.com/api/v1/tesla/oauth/callback is registered.

When a developer runs the API locally (on a dev box or in a GitHub Actions worker), Tesla has no way to deliver the OAuth callback to localhost:8100 because:

  1. Tesla's OAuth server redirects the user's browser to the registered callback URL
  2. The registered URL points to Cloud Run (dev.api.carpulsetracker.com)
  3. The local API instance never receives the callback
  4. The full OAuth-to-report flow cannot be tested end-to-end locally

1.2 Current State

PRODUCTION/DEV DEPLOYED FLOW (works):
=============================================================================

  Browser                    Tesla Auth              Cloud Run (dev)
    |                           |                        |
    |--- POST /oauth/initiate ->|                        |
    |<-- authUrl (tesla.com) ---|                        |
    |                           |                        |
    |--- GET auth.tesla.com --->|                        |
    |    (user logs in)         |                        |
    |<-- 302 redirect ---------|                        |
    |                           |                        |
    |--- GET /oauth/callback?code=xxx ----------------->|  <-- THIS WORKS
    |<-- 302 ?oauth=success ----------------------------|
    |                           |                        |

LOCAL DEVELOPMENT FLOW (broken):
=============================================================================

  Browser                    Tesla Auth              localhost:8100
    |                           |                        |
    |--- POST /oauth/initiate ->|                        |
    |<-- authUrl (tesla.com) ---|                        |
    |                           |                        |
    |--- GET auth.tesla.com --->|                        |
    |    (user logs in)         |                        |
    |<-- 302 redirect ---------|                        |
    |                           |                        |
    |--- GET dev.api.carpulsetracker.com/callback ------>| Cloud Run (dev)
    |                           |                   ^^^^^^^^^^^^^^^^^^^^^^^^^
    |                           |                   GOES TO CLOUD RUN,
    |                           |                   NOT TO LOCAL INSTANCE!
    |                           |                        |
    |    localhost:8100 NEVER receives the callback      |

1.3 Impact

Scenario Tesla OAuth Testable? Report Flow Testable?
Cloud Run (dev/tst/prd) YES YES
Local developer box NO NO (mock only)
GitHub Actions worker NO NO (mock only)

The mock server (con-bnc-cpt-tesla-mock:9100) helps with unit tests but cannot validate the real Tesla OAuth handshake, real token exchange, or real vehicle data fetching.


2. Proposed Solution: Dev Proxy Relay

2.1 Concept

Introduce a proxy relay mode in the deployed API (dev.api.carpulsetracker.com) that acts as a transparent bridge between Tesla and the developer's local API instance.

The deployed API becomes a thin relay — it receives Tesla's OAuth callback, then forwards it to the developer's registered local tunnel endpoint. The local API processes the callback, fetches vehicle data, and returns the result through the relay back to the user's browser.

2.2 Architecture Overview

                           +--------------------------+
                           |   Tesla Auth Server      |
                           |   auth.tesla.com         |
                           +-----------+--------------+
                                       |
                           (2) User authenticates
                           (3) Tesla redirects to
                               registered callback URL
                                       |
                                       v
+----------+              +---------------------------+              +-----------------+
|          |  (1) initiate|  Cloud Run (dev)           |  (4) relay  |  Local API      |
|  Browser | ------------>|  dev.api.carpulsetracker   | ----------->|  localhost:8100 |
|          |              |  .com                      |             |  (or GH worker) |
|          |              |                            |             |                 |
|          |              |  /api/v1/tesla/oauth/      |  (5) local  |  Processes      |
|          |              |    callback?code=xxx       |  processes  |  OAuth callback  |
|          |              |                            |<------------|  Returns result |
|          |<-------------|  (6) Relay response back   |             |                 |
|          |   redirect   |  to browser                |             |                 |
+----------+              +---------------------------+              +-----------------+
                                       |
                                       | (4) Also talks to Tesla
                                       |     Fleet API for token
                                       |     exchange & data fetch
                                       v
                           +--------------------------+
                           |   Tesla Fleet API        |
                           |   fleet-api.prd.eu.vn.   |
                           |   cloud.tesla.com        |
                           +--------------------------+

2.3 Key Design Decisions

Decision Choice Rationale
Tunnel technology Cloudflare Tunnel (cloudflared) Free, stable, no random URLs — provides deterministic subdomain
Relay trigger X-Dev-Proxy-Target header or /proxy/register endpoint Developer registers their tunnel URL with the deployed API
Session isolation Per-state proxy routing Each OAuth state token maps to a specific developer's tunnel
Security Shared secret + HMAC Only authorized developers can register proxy targets
Scope dev environment only Proxy relay is disabled in tst/prd via env var

3. Detailed Design

3.1 Component: Proxy Registry (in bnc-cpt-api)

A new FastAPI router /api/v1/dev-proxy/ provides:

POST /api/v1/dev-proxy/register     — Register a local tunnel endpoint
DELETE /api/v1/dev-proxy/unregister  — Remove registration
GET /api/v1/dev-proxy/status         — Check active registrations

Registration payload:

{
  "tunnel_url": "https://my-dev-abc123.trycloudflare.com",
  "developer_id": "ysg",
  "hmac_signature": "<HMAC-SHA256 of tunnel_url + timestamp using shared secret>"
}

Storage: In-memory dict with TTL (same pattern as TeslaFleetService._states). No database needed — registrations are ephemeral (TTL: 4 hours).

3.2 Component: Callback Relay Middleware (in bnc-cpt-api)

When the /api/v1/tesla/oauth/callback endpoint receives a request:

  1. Check if the state parameter maps to a registered proxy target
  2. If proxy target exists: Forward the entire request to the tunnel URL
  3. If no proxy target: Process locally (normal Cloud Run flow)
# Pseudocode for relay logic in tesla.py callback handler
async def handle_tesla_oauth_callback(request, code, state, ...):
    # Check if this state was initiated by a proxied developer
    proxy_target = DevProxyRegistry.get_target_for_state(state)

    if proxy_target:
        # RELAY MODE: Forward to developer's local API via tunnel
        return await relay_callback_to_target(proxy_target, request)
    else:
        # NORMAL MODE: Process locally on Cloud Run
        return await process_callback_locally(code, state, ...)

3.3 Component: Tunnel Client (shell action in bnc-cpt-utl)

A new shell action do_start_dev_proxy_tunnel that:

  1. Starts cloudflared tunnel exposing localhost:8100
  2. Registers the tunnel URL with dev.api.carpulsetracker.com/api/v1/dev-proxy/register
  3. Overrides local TESLA_REDIRECT_URI to use the production callback URL (since callbacks will be relayed through Cloud Run)

4. Control Flow Diagrams

4.1 Developer Starts Local Dev Session

Developer's Machine                        Cloud Run (dev)
=====================                      =====================

$ ./run -a do_start_dev_proxy_tunnel
        |
        v
[1] Start cloudflared tunnel
    cloudflared tunnel --url localhost:8100
        |
        v
[2] Tunnel assigns URL
    https://dev-ysg-abc123.trycloudflare.com
        |
        v
[3] Register with Cloud Run
    POST https://dev.api.carpulsetracker.com
         /api/v1/dev-proxy/register
    Body: {
      tunnel_url: "https://dev-ysg-abc123...",
      developer_id: "ysg",
      hmac_signature: "..."
    }
        |                                          |
        +----------------------------------------->|
                                                   |
                                            [4] Validate HMAC
                                            [5] Store registration:
                                                dev_proxies["ysg"] = {
                                                  tunnel_url: "https://...",
                                                  registered_at: now(),
                                                  ttl: 4h
                                                }
                                                   |
        |<-----------------------------------------+
        v                                   200 OK
[6] Registration confirmed
    "Dev proxy active for ysg"
        |
        v
[7] Start local API (uvicorn)
    TESLA_REDIRECT_URI=https://dev.api.carpulsetracker.com/...
    python run.py
        |
        v
[8] Ready: local API serves on :8100
    Tunnel exposes it publicly
    Tesla callbacks will be relayed

4.2 OAuth Flow with Proxy Relay (Happy Path)

Browser          Cloud Run (dev)        Tesla Auth        Local API (:8100)     Tesla Fleet API
   |                   |                    |                   |                      |
   |  [1] POST /oauth/initiate (via tunnel)                    |                      |
   |------------------>| - - relay - - - ->|                   |                      |
   |                   |                   |                    |                      |
   |                   |         [2] Forward to local API       |                      |
   |                   |-------------------------------------->|                      |
   |                   |                   |                    |                      |
   |                   |                   |  [3] initiate_oauth()                    |
   |                   |                   |  state="abc123"    |                      |
   |                   |                   |  Register state -> proxy_target           |
   |                   |                   |                    |                      |
   |                   |  [4] Return authUrl                   |                      |
   |                   |<--------------------------------------|                      |
   |<------------------|                   |                    |                      |
   |                   |                   |                    |                      |
   |  [5] Redirect to auth.tesla.com       |                    |                      |
   |-------------------------------------->|                    |                      |
   |                   |                   |                    |                      |
   |  [6] User logs in, grants consent     |                    |                      |
   |<--------------------------------------|                    |                      |
   |                   |                   |                    |                      |
   |  [7] Tesla redirects to registered callback URL            |                      |
   |  GET dev.api.carpulsetracker.com/api/v1/tesla/oauth/callback?code=XYZ&state=abc123
   |------------------>|                   |                    |                      |
   |                   |                   |                    |                      |
   |                   |  [8] Lookup: state "abc123" has proxy target                  |
   |                   |                   |                    |                      |
   |                   |  [9] RELAY: Forward callback to tunnel                        |
   |                   |-------------------------------------->|                      |
   |                   |                   |                    |                      |
   |                   |                   |  [10] validate_state("abc123")            |
   |                   |                   |  [11] Exchange code for token              |
   |                   |                   |       POST tesla token endpoint            |
   |                   |                   |                    |--------------------->|
   |                   |                   |                    |<---------------------|
   |                   |                   |                    |  token_data          |
   |                   |                   |                    |                      |
   |                   |                   |  [12] Fetch vehicle data                  |
   |                   |                   |       GET /api/1/vehicles                  |
   |                   |                   |       GET /api/1/vehicles/{id}/data        |
   |                   |                   |                    |--------------------->|
   |                   |                   |                    |<---------------------|
   |                   |                   |                    |  vehicle data        |
   |                   |                   |                    |                      |
   |                   |                   |  [13] Store report, return session_id     |
   |                   |                   |                    |                      |
   |                   |  [14] Response: redirect to WUI       |                      |
   |                   |<--------------------------------------|                      |
   |                   |                   |                    |                      |
   |  [15] Relay redirect back to browser  |                    |                      |
   |<------------------|                   |                    |                      |
   |                   |                   |                    |                      |
   |  [16] Browser loads WUI ?oauth=success&session_id=xyz     |                      |
   |                   |                   |                    |                      |
   |  [17] GET /tesla/report/{session_id}  |                    |                      |
   |------------------>| - - relay - - - ->|                   |                      |
   |                   |-------------------------------------->|                      |
   |                   |<--------------------------------------|                      |
   |<------------------|  [18] Full vehicle report returned     |                      |
   |                   |                   |                    |                      |

4.3 GitHub Actions Worker Flow

GitHub Actions Runner                    Cloud Run (dev)         Tesla Auth
========================                 =====================   =============

[1] CI workflow starts
    .github/workflows/ci-tesla-e2e.yaml
        |
        v
[2] Clone repos, build containers
    make do-setup-api-no-cache
        |
        v
[3] Start cloudflared tunnel in CI
    cloudflared tunnel --url http://con-bnc-cpt-api:8100
        |
        v
[4] Register tunnel with Cloud Run
    POST dev.api.carpulsetracker.com/api/v1/dev-proxy/register
    Body: {
      tunnel_url: "https://ci-run-12345.trycloudflare.com",
      developer_id: "github-ci-${{ github.run_id }}",
      hmac_signature: "..."
    }
        |                                       |
        +-------------------------------------->|
                                                |  [5] Store CI registration
        |<--------------------------------------+
        v
[6] Run e2e Tesla OAuth test
    - Puppeteer opens WUI at localhost:3333
    - Clicks "Connect Tesla"
    - OAuth flow executes:
      * Tesla callback hits Cloud Run
      * Cloud Run relays to CI tunnel
      * CI API processes callback
      * Fetches real Tesla data
    - Test verifies report generated
        |
        v
[7] Deregister tunnel
    DELETE dev.api.carpulsetracker.com/api/v1/dev-proxy/unregister
        |
        v
[8] Cleanup, report results

5. Data Flow Diagrams

5.1 Registration Data Flow

+-------------------+        +-------------------+        +---------------------+
|  Developer Box    |        |  Cloud Run (dev)  |        |  In-Memory Store    |
|  or CI Runner     |        |  FastAPI          |        |  (DevProxyRegistry) |
+-------------------+        +-------------------+        +---------------------+
        |                            |                            |
        |  POST /dev-proxy/register  |                            |
        |  {                         |                            |
        |    tunnel_url,             |                            |
        |    developer_id,           |                            |
        |    hmac_signature,         |                            |
        |    timestamp               |                            |
        |  }                         |                            |
        |--------------------------->|                            |
        |                            |                            |
        |                     [Validate HMAC]                     |
        |                     [Check timestamp                    |
        |                      freshness < 60s]                   |
        |                            |                            |
        |                            |  store(developer_id, {     |
        |                            |    tunnel_url,             |
        |                            |    registered_at,          |
        |                            |    ttl: 4h                 |
        |                            |  })                        |
        |                            |--------------------------->|
        |                            |                            |
        |                            |  201 Created               |
        |<---------------------------|                            |
        |                            |                            |

5.2 State-to-Proxy Mapping Data Flow

When a proxied developer calls POST /oauth/initiate:

+-------------------+        +---------------------+        +---------------------+
|  Local API        |        |  DevProxyRegistry   |        |  TeslaFleetService  |
|  (via tunnel)     |        |  (state → target)   |        |  (_states dict)     |
+-------------------+        +---------------------+        +---------------------+
        |                            |                            |
        | initiate_oauth(            |                            |
        |   paymentId, plan)         |                            |
        |                            |                            |
        |                     [Generate state token]              |
        |                     state = "abc123..."                 |
        |                            |                            |
        |                     [Map state to proxy target]         |
        |                     state_proxy_map["abc123"] = {       |
        |                       tunnel_url: "https://...",        |
        |                       developer_id: "ysg"               |
        |                     }                                   |
        |                            |--------------------------->|
        |                            |  Also store in _states:    |
        |                            |  _states["abc123"] = {     |
        |                            |    paymentId, plan,        |
        |                            |    created_at, proxy_target |
        |                            |  }                         |
        |                            |                            |
        |<---------------------------|                            |
        | authUrl with state=abc123  |                            |
        |                            |                            |

5.3 Callback Relay Data Flow

+----------+     +-----------------------+     +------------------+     +--------------+
| Browser  |     | Cloud Run (dev)       |     | Tunnel (cfd)     |     | Local API    |
|          |     | Relay Middleware       |     | *.trycloudflare  |     | :8100        |
+----------+     +-----------------------+     +------------------+     +--------------+
     |                    |                           |                       |
     | GET /callback      |                           |                       |
     | ?code=XYZ          |                           |                       |
     | &state=abc123      |                           |                       |
     |------------------->|                           |                       |
     |                    |                           |                       |
     |              [Lookup state "abc123"             |                       |
     |               in state_proxy_map]              |                       |
     |                    |                           |                       |
     |              [Found! proxy_target =            |                       |
     |               "https://dev-ysg-..."]           |                       |
     |                    |                           |                       |
     |              [Build relay request:             |                       |
     |               GET {tunnel}/api/v1/tesla/       |                       |
     |                 oauth/callback?code=XYZ        |                       |
     |                 &state=abc123                  |                       |
     |               Headers: X-Forwarded-For,        |                       |
     |                 X-Relay-From, X-Relay-Hmac]    |                       |
     |                    |                           |                       |
     |                    |--- HTTPS GET ------------>|                       |
     |                    |                           |--- HTTP GET --------->|
     |                    |                           |                       |
     |                    |                           |  [Process callback:   |
     |                    |                           |   validate_state()    |
     |                    |                           |   exchange code       |
     |                    |                           |   fetch vehicle data  |
     |                    |                           |   store report]       |
     |                    |                           |                       |
     |                    |                           |<-- 302 Redirect ------|
     |                    |                           |  Location: frontend   |
     |                    |                           |  ?oauth=success       |
     |                    |                           |  &session_id=xyz      |
     |                    |<--- HTTPS 302 ------------|                       |
     |                    |                           |                       |
     |              [Relay: pass through              |                       |
     |               the 302 redirect to browser]     |                       |
     |                    |                           |                       |
     |<--- 302 Redirect --|                           |                       |
     |  Location:         |                           |                       |
     |  localhost:3333    |                           |                       |
     |  ?oauth=success    |                           |                       |
     |  &session_id=xyz   |                           |                       |
     |                    |                           |                       |

5.4 Complete Data Entity Map

+========================================================================================+
|                           DATA ENTITIES & THEIR LOCATIONS                                |
+========================================================================================+
|                                                                                         |
|  CLOUD RUN (dev.api.carpulsetracker.com)                                               |
|  +------------------------------------------------------------------+                  |
|  |  DevProxyRegistry (in-memory, TTL: 4h)                           |                  |
|  |  +------------------------------------------------------------+  |                  |
|  |  |  dev_proxies = {                                           |  |                  |
|  |  |    "ysg": {                                                |  |                  |
|  |  |      tunnel_url: "https://dev-ysg-abc.trycloudflare.com", |  |                  |
|  |  |      registered_at: "2026-02-16T10:00:00Z",               |  |                  |
|  |  |      ttl: 14400  (4 hours)                                 |  |                  |
|  |  |    },                                                      |  |                  |
|  |  |    "github-ci-12345": {                                    |  |                  |
|  |  |      tunnel_url: "https://ci-12345.trycloudflare.com",    |  |                  |
|  |  |      registered_at: "2026-02-16T11:30:00Z",               |  |                  |
|  |  |      ttl: 14400                                            |  |                  |
|  |  |    }                                                       |  |                  |
|  |  |  }                                                         |  |                  |
|  |  +------------------------------------------------------------+  |                  |
|  |                                                                  |                  |
|  |  state_proxy_map = {                                             |                  |
|  |    "state_token_abc123": {                                       |                  |
|  |      developer_id: "ysg",                                        |                  |
|  |      tunnel_url: "https://dev-ysg-abc.trycloudflare.com"        |                  |
|  |    }                                                             |                  |
|  |  }                                                               |                  |
|  +------------------------------------------------------------------+                  |
|                                                                                         |
|  LOCAL API (localhost:8100)                                                             |
|  +------------------------------------------------------------------+                  |
|  |  TeslaFleetService._states = {                                   |                  |
|  |    "state_token_abc123": {                                       |                  |
|  |      paymentId: "pi_test_123",                                   |                  |
|  |      plan: "basic",                                              |                  |
|  |      created_at: "2026-02-16T10:05:00Z"                         |                  |
|  |    }                                                             |                  |
|  |  }                                                               |                  |
|  |                                                                  |                  |
|  |  TeslaFleetService._reports = {                                  |                  |
|  |    "session_xyz789": {                                           |                  |
|  |      report: { vehicle: {...}, vehicleData: {...}, ... },        |                  |
|  |      state_data: { paymentId: "pi_test_123", plan: "basic" },   |                  |
|  |      created_at: "2026-02-16T10:05:30Z"                         |                  |
|  |    }                                                             |                  |
|  |  }                                                               |                  |
|  +------------------------------------------------------------------+                  |
|                                                                                         |
+========================================================================================+

6. Execution Flow — Step-by-Step Implementation

6.1 Phase 1: API Changes (bnc-cpt-api)

6.1.1 New File: app/services/dev_proxy.py

Purpose: DevProxyRegistry class — manages tunnel registrations and state-to-proxy mappings
Pattern: Follows TeslaFleetService pattern (class-level dicts, TTL cleanup)

Key methods:
  register(tunnel_url, developer_id, hmac_sig) -> bool
  unregister(developer_id) -> bool
  get_target_for_state(state) -> Optional[str]
  map_state_to_proxy(state, developer_id) -> None
  cleanup_expired() -> None
  is_enabled() -> bool   (checks DEV_PROXY_ENABLED env var)

6.1.2 New File: app/api/v1/routers/dev_proxy.py

Purpose: REST endpoints for proxy registration

Endpoints:
  POST   /api/v1/dev-proxy/register     — Register tunnel URL
  DELETE /api/v1/dev-proxy/unregister   — Remove registration
  GET    /api/v1/dev-proxy/status        — List active registrations

6.1.3 Modified File: app/api/v1/routers/tesla.py

Changes to handle_tesla_oauth_callback():
  - Before processing: check DevProxyRegistry.get_target_for_state(state)
  - If proxy target found: relay request via httpx to tunnel URL
  - If no proxy target: process normally (existing code, unchanged)

Changes to initiate_tesla_oauth():
  - If request came through proxy (X-Relay-From header): register state mapping

6.1.4 Modified File: app/config.py

New settings:
  DEV_PROXY_ENABLED: bool = False          # Must be True to activate
  DEV_PROXY_SHARED_SECRET: Optional[str]   # HMAC shared secret
  DEV_PROXY_MAX_TTL: int = 14400           # 4 hours in seconds

6.2 Phase 2: Configuration Changes (bnc-cpt-cnf)

6.2.1 Modified: dev.env.yaml

# Add to step 029-create-gcp-secrets:
DEV_PROXY_SHARED_SECRET:
  secret_name: "bnc-cpt-dev-proxy-shared-secret"
  version: "latest"

# Add to step 030-gcp-cloud-run environment_variables:
DEV_PROXY_ENABLED: "True"    # Only in dev!

6.2.2 NOT modified: tst.env.yaml, prd.env.yaml

# DEV_PROXY_ENABLED defaults to False — proxy is disabled in tst/prd

6.3 Phase 3: Shell Actions (bnc-cpt-utl)

6.3.1 New: src/bash/run/start-dev-proxy-tunnel.func.sh

do_start_dev_proxy_tunnel()
  # 1. Check cloudflared is installed
  # 2. Start tunnel: cloudflared tunnel --url localhost:${API_PORT_HOST:-8100}
  # 3. Capture tunnel URL from cloudflared output
  # 4. Register with Cloud Run: POST /api/v1/dev-proxy/register
  # 5. Export TESLA_REDIRECT_URI=https://dev.api.carpulsetracker.com/...
  # 6. Print status and instructions

6.3.2 New: src/bash/run/stop-dev-proxy-tunnel.func.sh

do_stop_dev_proxy_tunnel()
  # 1. Deregister from Cloud Run: DELETE /api/v1/dev-proxy/unregister
  # 2. Kill cloudflared process
  # 3. Print confirmation

6.3.3 New: src/bash/run/ci-start-dev-proxy.func.sh

do_ci_start_dev_proxy()
  # 1. Install cloudflared (apt-get or binary download)
  # 2. Start tunnel to container API (http://con-bnc-cpt-api:8100)
  # 3. Register with Cloud Run
  # 4. Used by GitHub Actions workflow for e2e Tesla tests

6.4 Phase 4: Docker Changes (bnc-cpt-utl)

6.4.1 Modified: src/docker/.env

# Add:
DEV_PROXY_ENABLED=True
DEV_PROXY_SHARED_SECRET=local-dev-shared-secret-change-in-prod

6.4.2 Modified: src/docker/docker-compose-app.yaml

# Add to cpt-api service environment:
DEV_PROXY_ENABLED: "${DEV_PROXY_ENABLED:-False}"
DEV_PROXY_SHARED_SECRET: "${DEV_PROXY_SHARED_SECRET:-}"

7. Security Design

7.1 Threat Model

Threat Mitigation
Unauthorized proxy registration HMAC-SHA256 signature with shared secret
Replay attacks Timestamp in HMAC payload, 60s freshness window
Proxy target hijacking Registration overwrites by developer_id (latest wins)
Data exfiltration via rogue tunnel Only dev environment; tst/prd have proxy DISABLED
Stale registrations Auto-expire after TTL (4 hours)
SSRF via tunnel_url Validate URL format; only HTTPS allowed; only *.trycloudflare.com

7.2 HMAC Signature Scheme

message = f"{tunnel_url}:{developer_id}:{timestamp_unix}"
signature = HMAC-SHA256(shared_secret, message)

Validation:
  1. Recompute HMAC with stored shared secret
  2. Compare signatures (constant-time comparison)
  3. Check timestamp is within 60 seconds of server time

7.3 Environment Guard

# In dev_proxy.py
class DevProxyRegistry:
    @classmethod
    def is_enabled(cls) -> bool:
        return settings.DEV_PROXY_ENABLED and settings.ENV in ("dev", "lcl", "inf")

The proxy relay is impossible to activate in tst or prd environments, even if someone sets DEV_PROXY_ENABLED=True, because the environment guard also checks the ENV variable.


8. Alternative Approaches Considered

8.1 Option A: ngrok / Random Tunnel URLs

Rejected. Tesla requires pre-registered callback URLs. Random tunnel URLs cannot be registered with Tesla's developer portal.

8.2 Option B: Polling-Based Relay (Store-and-Forward)

Cloud Run receives callback → stores code+state in GCS bucket
Local API polls GCS bucket → picks up callback → processes it

Rejected. Adds latency (polling interval), requires GCS access from local, and complicates the browser redirect flow (browser waits on Cloud Run, which waits for local to process and signal completion).

8.3 Option C: WebSocket Relay

Local API maintains WebSocket to Cloud Run
Cloud Run forwards callbacks over WebSocket in real-time

Rejected. Cloud Run has a 60-minute WebSocket timeout. Developers would need to reconnect periodically. Adds complexity for marginal benefit over HTTP relay.

8.4 Option D: DNS Override (hosts file + SSH tunnel)

/etc/hosts: 127.0.0.1 dev.api.carpulsetracker.com
SSH tunnel to forward port 443 to localhost:8100

Rejected. Requires root/sudo for hosts file and port 443. Breaks other team members' ability to reach the real dev API. Does not work in CI.

8.5 Option E: Separate Tesla App per Developer

Rejected. Tesla developer portal limits the number of apps. Each app requires separate review. Not scalable for team development.


9. Testing Strategy

9.1 Unit Tests (bnc-cpt-api)

tests/test_dev_proxy.py
  - test_register_valid_hmac → 201
  - test_register_invalid_hmac → 403
  - test_register_expired_timestamp → 403
  - test_unregister → 200
  - test_proxy_disabled_returns_404
  - test_callback_with_proxy_state_relays
  - test_callback_without_proxy_state_processes_locally
  - test_registration_ttl_expiry
  - test_only_https_tunnel_urls_accepted
  - test_ssrf_protection_rejects_non_cloudflare_urls

9.2 Integration Test (bnc-cpt-utl, Puppeteer)

test/specs/test-tesla-oauth-e2e.spec.js
  - Start tunnel
  - Register proxy
  - Navigate WUI → Payment → OAuth
  - Complete Tesla login (test account)
  - Verify callback relayed to local API
  - Verify report displayed in WUI

9.3 CI Smoke Test (GitHub Actions)

# .github/workflows/ci-tesla-e2e.yaml
# Triggered: manual (workflow_dispatch) — not on every push
# Reason: requires real Tesla test credentials

steps:
  - name: Start tunnel and proxy
    run: ./run -a do_ci_start_dev_proxy
  - name: Run Tesla e2e test
    run: make test-api-tesla-e2e
  - name: Cleanup
    run: ./run -a do_ci_stop_dev_proxy

10. Files Changed Summary

New Files

Project Path Purpose
bnc-cpt-api app/services/dev_proxy.py Proxy registry service
bnc-cpt-api app/api/v1/routers/dev_proxy.py REST endpoints for proxy mgmt
bnc-cpt-api tests/test_dev_proxy.py Unit tests for proxy
bnc-cpt-utl src/bash/run/start-dev-proxy-tunnel.func.sh Shell action: start tunnel
bnc-cpt-utl src/bash/run/stop-dev-proxy-tunnel.func.sh Shell action: stop tunnel
bnc-cpt-utl src/bash/run/ci-start-dev-proxy.func.sh Shell action: CI tunnel setup
bnc-cpt-utl src/bash/run/ci-stop-dev-proxy.func.sh Shell action: CI tunnel teardown
bnc-cpt-utl doc/md/proxy-proposal.md This document

Modified Files

Project Path Change
bnc-cpt-api app/config.py Add DEV_PROXY_* settings
bnc-cpt-api app/api/v1/routers/tesla.py Add relay check in callback
bnc-cpt-api app/main.py Include dev_proxy router
bnc-cpt-cnf bnc-cpt/dev.env.yaml Add DEV_PROXY_ENABLED, secret
bnc-cpt-utl src/docker/.env Add DEV_PROXY_* env vars
bnc-cpt-utl src/docker/docker-compose-app.yaml Pass DEV_PROXY_* to container

11. Rollout Plan

Phase 1: Foundation (1-2 days)

Phase 2: Relay Logic (1-2 days)

Phase 3: Shell Actions & Docker (1 day)

Phase 4: CI Integration (1 day)

Phase 5: Configuration & Secrets (1 day)


12. Dependencies & Prerequisites

Dependency Status Action
cloudflared binary Not installed Add to do_check_install_cloudflared shell action
Tesla test account credentials Available in GCP Secrets Already provisioned
DEV_PROXY_SHARED_SECRET Not yet created Create in GCP Secret Manager (dev env)
httpx (async HTTP client) Already in bnc-cpt-api Used by TeslaFleetService
Cloud Run (dev) redeployment Required After API changes, redeploy via CI

13. Success Criteria

  1. Developer runs ./run -a do_start_dev_proxy_tunnel on their local box
  2. Opens localhost:3333 in browser
  3. Completes the full payment → Tesla OAuth → vehicle report flow
  4. Tesla's OAuth callback is relayed through Cloud Run to the local API
  5. The vehicle report is generated from real Tesla data
  6. The entire flow works without any manual DNS or hosts file changes
  7. The same flow works in GitHub Actions for automated e2e testing
  8. No changes affect tst or prd environments
  9. Existing unit tests continue to pass (proxy is transparent when disabled)