Group: Publishing & Infrastructure · Demonstrated: 2026-05-18
Each client portal (vg, bc, rwp) is a tenant in one shared codebase. A new tenant is a config block + two logo files + a content tree.
The architecture
02_active/<tenant>/published/ ← Drive canonical source
↓ (sync_portal.py + watchdog + debounce)
~/Sites/<tenant>-portal/ ← git deploy target
↓ (git push origin main)
GitHub randallosp/<tenant>-portal
↓ (Netlify deploy hook)
<tenant>.opsstrategypro.com OR <tenant>-portal.netlify.app
Shared infrastructure
| Component | What it does |
|---|---|
portal_index.py |
Generates index.html per tenant — sidebar + topbar + shell |
render_section_hub.py |
Renders curated section hubs (Skills, How-We-Work, Workflow, etc.) |
sync_portal.py |
Drive → portal repo → GitHub → Netlify, with watchdog FSEvents |
templates/portal-shell.html |
Shared shell template (password gate, sidebar, iframe viewer) |
templates/section-hub.html |
Shared card-hub template |
templates/bc-page.html |
Per-doc page wrapper (used by State, Digest, detail pages) |
What's tenant-specific
| Per-tenant artifact | Where |
|---|---|
| Tenant config block | _tenant_configs() in portal_index.py |
| Password hash (SHA-256) | tenant_password_hash in config block |
| Logo files | templates/_<tenant>-portal-button.b64.txt + _<tenant>-portal-home.b64.txt |
| Sidebar group label | SIDEBAR_GROUP_LABEL in portal_index.py |
| Section ordering | SIDEBAR_ORDER_OVERRIDE in portal_index.py |
| Section filters | SECTION_FILTERS in portal_index.py |
| Content tree | 02_active/<tenant>/published/<section>/*.html |
Adding a new tenant — checklist
- Pick a tenant slug (e.g.,
acme). - Pick an access code; SHA-256 hash it; never store cleartext.
- Create
02_active/acme/(engagement folder convention). - Add tenant block to
_tenant_configs()inportal_index.py. - Set up
~/Sites/acme-portal/git repo, push to GitHub. - Create Netlify project, point at the GitHub repo.
- (Optional) Configure custom subdomain DNS.
- If using custom subdomain: provision the SSL cert in the Netlify
dashboard. Domain management → "Verify DNS configuration" →
"Provision certificate." Adding a CNAME does NOT auto-provision the
cert. Until the cert is issued, the password gate fails silently
because
crypto.subtle.digestis unavailable in insecure contexts (browser blocks the secure-context API when the cert is missing or the user clicked past an SSL warning). Takes 1–15 minutes. - Drop content into
02_active/acme/published/. - Restart sync daemon.
Where to look
02_active/vg/tools/portal_index.py— multi-tenant generator02_active/vg/tools/render_section_hub.py— section hub configsRWP/tools/sync_portal.py— sync daemon (per-tenant dict at top)