Summary
Enable authenticated access to Nodecore RPC through the local Obol Stack (obol.stack). Users authenticate via Google OAuth, and their ID token is stored in a Kubernetes secret. eRPC injects this token into upstream requests, allowing Nodecore to bypass rate limits for authenticated users.
Goals
- Single-user authentication for local development stack
- API key management via Kubernetes secrets
- Auditing/logging user identity at Nodecore
- Rate limit bypass for authenticated requests
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ https://obol.stack │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Browser │────────▶│ Traefik │ │
│ │ │ │ (TLS/mkcert) │ │
│ └──────────────┘ └────────┬────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Next.js Frontend │ │ eRPC │ │ Other │ │
│ │ (obol-frontend) │ │ /rpc/* │ │ Services │ │
│ └──────────┬──────────┘ └──────┬───────┘ └──────────────┘ │
│ │ │ │
│ │ writes │ reads env │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Kubernetes Secret: obol-oauth-token (namespace: erpc) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ eRPC injects header: X-Nodecore-Token │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Nodecore │ │
│ │ (validates JWT) │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Authentication Flow
Step-by-Step
- User opens
https://obol.stack
- Clicks "Sign in with Google"
- Browser initiates Google OAuth PKCE flow
- Google redirects to
https://obol.stack/api/auth/callback
- Next.js API route (server-side) exchanges code for tokens
- API route writes ID token to Kubernetes secret
obol-oauth-token
- eRPC pod reads token from secret (via environment variable)
- eRPC injects
X-Nodecore-Token: <id_token> on all upstream requests
- Nodecore validates JWT and bypasses rate limits
Important Clarification
The browser does NOT write directly to Kubernetes.
The Next.js API route (running server-side in the cluster) has Kubernetes API access and writes the secret.
Component Specifications
1. Ingress (Traefik)
| Requirement |
Implementation |
| HTTPS |
mkcert for locally-trusted TLS certificate |
| Domain |
obol.stack (via /etc/hosts) |
| OAuth Redirect |
https://obol.stack/api/auth/callback |
2. Frontend (Next.js)
New API Routes Required:
| Route |
Purpose |
GET /api/auth/login |
Initiates Google OAuth PKCE flow |
GET /api/auth/callback |
Handles OAuth callback, writes token to k8s secret |
POST /api/auth/refresh |
Refreshes token and updates secret |
POST /api/auth/logout |
Clears token from secret |
Token Management:
- Frontend must refresh token every 45 minutes
- Uses Google refresh token to obtain new ID token
- Updates Kubernetes secret after each refresh
Kubernetes RBAC Required:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: oauth-token-writer
namespace: erpc
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["obol-oauth-token"]
verbs: ["get", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: frontend-oauth-token-writer
namespace: erpc
subjects:
- kind: ServiceAccount
name: obol-frontend
namespace: obol-frontend
roleRef:
kind: Role
name: oauth-token-writer
apiGroup: rbac.authorization.k8s.io
3. eRPC Configuration
Header Injection (Native Support Confirmed):
eRPC supports custom headers on upstream requests via jsonRpc.headers with environment variable expansion using ${VAR} syntax.
# erpc.yaml
projects:
- id: main
upstreams:
- id: nodecore
endpoint: https://rpc.nodecore.io
jsonRpc:
headers:
X-Nodecore-Token: "${OBOL_OAUTH_TOKEN}"
Helm Values:
# values/erpc.yaml
secretEnv:
OBOL_OAUTH_TOKEN:
secretKeyRef:
name: obol-oauth-token
key: token
4. Token Rotation
Decision Required: eRPC Reload Mechanism
Current Limitation:
eRPC expands environment variables once at startup (os.ExpandEnv()). There is no SIGHUP handler or config hot-reload support.
Options:
| Option |
Latency |
Downtime |
Complexity |
Notes |
| A. Stakater Reloader |
~30s |
Zero (rolling) |
Low |
Recommended for simplicity |
| B. Contribute SIGHUP to eRPC |
Immediate |
Zero |
Medium |
Requires upstream contribution |
| C. Short pod TTL |
Variable |
Zero (rolling) |
Low |
Wasteful, not recommended |
| D. Sidecar token injector |
Immediate |
Zero |
High |
Custom development required |
Option A: Stakater Reloader (Will cause rolling restart))
apiVersion: apps/v1
kind: Deployment
metadata:
name: erpc
namespace: erpc
annotations:
secret.reloader.stakater.com/reload: "obol-oauth-token"
spec:
# ... triggers rolling restart when secret changes
Pros:
- No code changes to eRPC
- Battle-tested solution
- Zero downtime with readiness probes
Cons:
- ~30 second propagation delay
- Pod restart on every token refresh (every 45 min)
Option B: Contribute SIGHUP to eRPC
Add signal handler to eRPC upstream:
// Proposed change to cmd/erpc/main.go
sighup := make(chan os.Signal, 1)
signal.Notify(sighup, syscall.SIGHUP)
go func() {
for range sighup {
logger.Info().Msg("SIGHUP received, reloading config...")
cfg, _ = common.LoadConfig(fs, configPath, opts)
// Reinitialize upstreams with new config
}
}()
Then trigger via:
kubectl exec -n erpc deploy/erpc -- kill -HUP 1
Pros:
- Immediate token update
- No pod restart
- Benefits entire eRPC community
Cons:
- Requires upstream PR and approval
- Development and testing effort
5. Nodecore Requirements
JWT Validation:
| Field |
Expected Value |
iss (issuer) |
https://accounts.google.com |
aud (audience) |
Google OAuth Client ID |
exp (expiry) |
Must be in the future |
| Signature |
Validated against Google JWKS |
JWKS Endpoint: https://www.googleapis.com/oauth2/v3/certs
Rate Limit Behavior:
- Valid JWT → Skip rate limiting
- Invalid/missing JWT → Apply standard rate limits
6. Kubernetes Secret Schema
apiVersion: v1
kind: Secret
metadata:
name: obol-oauth-token
namespace: erpc
type: Opaque
data:
token: <base64-encoded-google-id-token>
Security Considerations
| Concern |
Mitigation |
| Token exposure in logs |
eRPC does not log header values at INFO level |
| Cross-namespace access |
RBAC limits frontend to single secret |
| Token theft from secret |
k8s secrets encrypted at rest (k3s default) |
| Man-in-the-middle |
TLS required for all traffic |
| Token expiry |
45-minute refresh cycle |
Implementation Phases
Phase 1: Infrastructure
Phase 2: Frontend OAuth
Phase 3: eRPC Configuration
Phase 4: Nodecore Integration
Open Decisions
| # |
Decision |
Options |
Owner |
Due |
| 1 |
eRPC reload mechanism |
A: Reloader, B: SIGHUP contribution |
Team |
TBD |
| 2 |
Google OAuth Client ID source |
Environment var vs ConfigMap |
Team |
TBD |
| 3 |
Token refresh location |
Frontend background task vs dedicated service |
Team |
TBD |
Appendix: eRPC Header Injection Evidence
From erpc/clients/http_json_rpc_client.go:790-793:
// Add custom headers if provided
for k, v := range c.headers {
httpReq.Header.Set(k, v)
}
From erpc/common/config.go:68:
expandedData := []byte(os.ExpandEnv(string(data)))
Environment variables using ${VAR} syntax are expanded at config load time.
Appendix: Google OAuth PKCE Flow
┌──────────┐ ┌──────────────┐
│ Browser │ │ Google │
└────┬─────┘ └──────┬───────┘
│ │
│ 1. GET /api/auth/login │
│──────────────────────────────────────────▶│
│ │
│ 2. Redirect to Google (code_challenge) │
│◀──────────────────────────────────────────│
│ │
│ 3. User authenticates │
│──────────────────────────────────────────▶│
│ │
│ 4. Redirect to callback (code) │
│◀──────────────────────────────────────────│
│ │
│ 5. POST /api/auth/callback │
│───────┐ │
│ │ Exchange code for tokens │
│ │ (with code_verifier) │
│◀──────┘ │
│ │
│ 6. Write token to k8s secret │
│ │
│ 7. Set session cookie, redirect to app │
│ │
Summary
Enable authenticated access to Nodecore RPC through the local Obol Stack (
obol.stack). Users authenticate via Google OAuth, and their ID token is stored in a Kubernetes secret. eRPC injects this token into upstream requests, allowing Nodecore to bypass rate limits for authenticated users.Goals
Architecture Overview
Authentication Flow
Step-by-Step
https://obol.stackhttps://obol.stack/api/auth/callbackobol-oauth-tokenX-Nodecore-Token: <id_token>on all upstream requestsImportant Clarification
Component Specifications
1. Ingress (Traefik)
obol.stack(via/etc/hosts)https://obol.stack/api/auth/callback2. Frontend (Next.js)
New API Routes Required:
GET /api/auth/loginGET /api/auth/callbackPOST /api/auth/refreshPOST /api/auth/logoutToken Management:
Kubernetes RBAC Required:
3. eRPC Configuration
Header Injection (Native Support Confirmed):
eRPC supports custom headers on upstream requests via
jsonRpc.headerswith environment variable expansion using${VAR}syntax.Helm Values:
4. Token Rotation
Decision Required: eRPC Reload Mechanism
Current Limitation:
eRPC expands environment variables once at startup (
os.ExpandEnv()). There is no SIGHUP handler or config hot-reload support.Options:
Option A: Stakater Reloader (Will cause rolling restart))
Pros:
Cons:
Option B: Contribute SIGHUP to eRPC
Add signal handler to eRPC upstream:
Then trigger via:
Pros:
Cons:
5. Nodecore Requirements
JWT Validation:
iss(issuer)https://accounts.google.comaud(audience)exp(expiry)JWKS Endpoint:
https://www.googleapis.com/oauth2/v3/certsRate Limit Behavior:
6. Kubernetes Secret Schema
Security Considerations
Implementation Phases
Phase 1: Infrastructure
obol-oauth-tokensecret (empty placeholder)Phase 2: Frontend OAuth
https://obol.stack/api/auth/callback)/api/auth/loginroute (PKCE flow initiation)/api/auth/callbackroute (token exchange + secret write)/api/auth/refreshroute (background refresh)Phase 3: eRPC Configuration
secretEnvforOBOL_OAUTH_TOKENX-Nodecore-Tokenheader to Nodecore upstream configPhase 4: Nodecore Integration
Open Decisions
Appendix: eRPC Header Injection Evidence
From
erpc/clients/http_json_rpc_client.go:790-793:From
erpc/common/config.go:68:Environment variables using
${VAR}syntax are expanded at config load time.Appendix: Google OAuth PKCE Flow