-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrender.yaml
More file actions
201 lines (187 loc) · 9.96 KB
/
Copy pathrender.yaml
File metadata and controls
201 lines (187 loc) · 9.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# Render Blueprint for llms.2plot.dev
#
# Applies to a new service with `render blueprint launch`, and documents the
# deployment. Values marked sync:false are per-deployment and must be set in
# the Render dashboard.
#
# INHERITED-IDENTITY CORRECTION, 2026-08-26: every identity field below said
# `boilerplate` until this sync — the template's own service name, domain and
# app key, carried through the fork untouched while production served
# llms.2plot.dev with `app: "llms"`. A blueprint that names another host is
# the same defect class as an inherited workflow that deploys one: harmless
# until someone applies it. The live service's dashboard values were always
# right; this file now agrees with them.
services:
- type: web
name: llms-2plot-dev
runtime: python
plan: starter
region: oregon
# `release` is written ONLY by CD (.github/workflows/cd.yml, job
# `deploy`: a fast-forward push of the run's sha after the matrix is
# green). A push to main is not a deploy. autoDeploy stays unset (on):
# Render reacting to this branch IS the mechanism (1.6.35).
branch: release
# Two commands, not one: markdown2dash 0.1.2 declares gunicorn<22 against
# the CVE-driven gunicorn>=23 floor in requirements.txt, so it is installed
# without its dependency graph. Same pair as the Dockerfile and CI.
buildCommand: pip install -r requirements.txt && pip install --no-deps markdown2dash==0.1.2
startCommand: gunicorn run:server -b 0.0.0.0:$PORT --workers 2 --timeout 60
healthCheckPath: /healthz
domains:
- llms.2plot.dev
envVars:
# THE value to get right. It drives <link rel="canonical">, sitemap.xml
# and the absolute URLs in llms.txt. A fork that leaves this unset
# inherits the boilerplate's own domain and tells Google it is a
# duplicate — so lib/constants.py refuses to boot on Render without it.
- key: APP_BASE_URL
value: https://llms.2plot.dev
# ONE fleet Python. `runtime: python` above is the NATIVE runtime, so
# this key is REQUIRED and Render demands the full X.Y.Z encoding — the
# MINOR must match the Dockerfile's `FROM python:3.14-slim` tag, and
# tests/test_python_version.py holds the two together. The patch is a
# human bump; the minor drifting apart is the class that pin exists for.
# (On a `runtime: docker` service this key must be ABSENT instead:
# nothing reads it there, and a string that looks like the platform's
# setting and can never be true is the same defect arriving as the fix.)
- key: PYTHON_VERSION
value: "3.14.7"
# flask | fastapi | quart. FastAPI and Quart need the matching dash
# extra installed and an ASGI start command (uvicorn), so switching this
# alone is not enough.
- key: DASH_BACKEND
value: flask
# Dash 4.3+ MCP server. Off by default: it is a live introspection
# surface on a public host.
- key: DASH_MCP_ENABLED
value: "0"
# Network analytics — the signed rollup POSTed to 2plot.ai.
# Without the secret, reporting is off and the app says so at startup.
- key: CROSS_APP_WEBHOOK_SECRET
sync: false
# 15 minutes, not the code default of hourly: the fleet is on paid
# instances now and the hub board reads near-real-time. The live
# "active now" number rides the separate presence beacon
# (SATELLITE_PRESENCE_INTERVAL_S, default 60s, 0 disables) — this knob
# only paces the daily rollup.
- key: SATELLITE_REPORT_INTERVAL_S
value: "900"
# Also the bulletin's identity to the hub (lib/bulletin.py), so a fork
# that changes this stops receiving the boilerplate's announcements.
- key: SATELLITE_APP_KEY
value: llms
# This app's identity to the 2plot.dev ad network (lib/ad_client.py).
# Set explicitly rather than left to the code default so the value is
# visible here — and deliberately the SAME short key as
# SATELLITE_APP_KEY above, so /admin/ad-board, /traffic and the bulletin
# all name this app identically.
- key: AD_APP_ID
value: llms
# Hub-published tips and announcements in the llms.txt viewer header.
# Unset, the header still renders on the package's defaults with an
# "No announcements." empty state — no error either way, so the boot log
# line "network bulletin: ..." is how you tell which state you are in.
- key: NETWORK_BULLETIN_URL
value: https://2plot.dev/api/network/bulletin
# Behind Cloudflare the country header is already present, so the
# ip-api.com lookup is redundant latency.
- key: ANALYTICS_GEO_LOOKUP
value: "0"
# Access tiers for the tiered corpus documents (/llms-small.txt,
# /llms-full.txt — dash-improve-my-llms >= 2.4.0). Deliberately set to
# the default here so every fork SEES the knob: the 402 experiment
# tightens the full corpus by flipping this to `auth`, per satellite,
# with no code change. See .env.example for the tier values.
- key: LLMS_SMALL_TIER
value: public
- key: LLMS_FULL_TIER
value: public
# --- The interactive gate (lib/gate_layouts.py) ---------------------
# THE per-host gate switch: flip to `auth` and every docs page whose
# frontmatter doesn't pin a tier shows the sign-in card to anonymous
# browsers. Rollback is flipping it back — env only, no code revert.
# Funnel pages (/, /getting-started, the corpus pseudo-paths) are
# pinned public in code/frontmatter and never inherit this.
- key: PAGE_DEFAULT_TIER
value: public
# The second axis, and the phase-4 agent flip: while "1", machine
# surfaces (llms.txt family, crawler HTML, the prerender) stay open on
# auth pages so the crawl-demand window keeps measuring. "0" gates
# anonymous machine fetches too (keyed agents still pass). Do not flip
# before the window closes — see kickoff/fleet/KICKOFF-fleet-check.md.
- key: LLMS_PUBLIC_DEFAULT
value: "1"
# --- Clerk satellite auth (lib/auth.py) -----------------------------
# All OPTIONAL: absent, the site runs fully public and admin surfaces
# fail CLOSED (lib.auth.admin_access_open). Set the two keys to turn
# auth on. Values below mirror leaflet.2plot.dev, the pilot host.
- key: CLERK_SECRET_KEY
sync: false
- key: CLERK_PUBLISHABLE_KEY
sync: false
# The 2plot.ai PRIMARY on its production Clerk instance (Account
# Portal + Frontend API custom domains — not *.accounts.dev).
- key: CLERK_SIGN_IN_URL
value: https://accounts.2plot.ai/sign-in
- key: CLERK_SIGN_UP_URL
value: https://accounts.2plot.ai/sign-up
# THE RETURN TRIP — the stage-5 blocker leaflet's pilot surfaced
# (2026-08-21 handoff). Unset, buildSatelliteRedirect() returns null,
# every sign-in path falls through to the Clerk Account Portal, the
# hub's return machinery never runs, and the user authenticates
# successfully and STAYS on the primary — no error anywhere.
# A DESTINATION, not a flag: the value is concatenated as
# `<value>?returnTo=<here>`, so `true` or a bare host resolves against
# THIS site and the button 404s — worse than unset. lib/auth.py warns
# at boot for both mistakes; the absence of those warnings is the
# deploy acceptance check.
- key: CLERK_SATELLITE_SIGN_IN_REDIRECT
value: https://2plot.ai/onboarding
# REQUIRED in satellite mode; a production custom-domain instance
# cannot derive it from CLERK_SIGN_IN_URL.
- key: CLERK_FRONTEND_API
value: https://clerk.2plot.ai
# The REGISTERED SATELLITE DOMAIN — deliberately NOT the host this app
# is served on. Every *.2plot.dev docs host is an ALLOWED SUBDOMAIN of
# the 2plot.dev satellite; clerk.<your-host>.2plot.dev is NXDOMAIN and
# setting it here hangs sign-in. Session cookies are scoped to
# .2plot.dev, which is exactly what the allowlist enables.
- key: CLERK_SATELLITE_DOMAIN
value: 2plot.dev
- key: CLERK_IS_SATELLITE
value: "true"
# Signs the session + __dca_identity cookies. Without it
# dash-clerk-auth falls back to a PUBLIC dev default string.
- key: SESSION_SECRET
generateValue: true
# Admin allowlist for admin-tier pages (lib.auth.is_admin_user).
- key: ADMIN_EMAILS
sync: false
# The hub takes the LAST report for a given (app, date). On an ephemeral
# filesystem a mid-day deploy wipes the ledger and the next hourly report
# overwrites the day's real total with whatever accrued since the
# restart. The disk below is what stops that.
- key: TRAFFIC_ANALYTICS_FILE
value: /var/data/visitor_analytics.json
# Control-board overrides (/admin/control-board) persist here — on the
# same disk, so toggles survive deploys. TWICE on the pilot host this
# row rode render.yaml without reaching the live service (Blueprint env
# applies only on a sync) and every toggle silently reset per deploy;
# the boot now prints a [visibility] WARNING when the variable is
# missing OR when /var/data is not actually a mounted disk. The absence
# of that warning in a deploy log is the acceptance check.
- key: PAGE_VISIBILITY_FILE
value: /var/data/page_visibility.json
# The writable policy store behind the package's CALLABLE seams — the
# geo denylist and the per-vendor policy the control board mutates.
# Same disk, same reason as the two rows above: unset, it falls back to
# the app directory on an ephemeral filesystem and every toggle resets
# on the next deploy. lib/policy_store.py warns loudly at boot when it
# is missing OR when /var/data is not actually a mounted disk.
- key: POLICY_STORE_FILE
value: /var/data/policy_overrides.json
disk:
name: analytics
mountPath: /var/data
sizeGB: 1