Compare commits

..

27 Commits

Author SHA1 Message Date
will.anderson 3d6affff86 Merge pull request 'Correct Safety section to claim only what the product does' (#160) from fix/safety-claims-match-shipped-behavior into main
Deploy marketing to Cloud Run / deploy (push) Failing after 14m32s
2026-08-16 13:19:29 +00:00
will.anderson bbedb1daba Merge pull request 'feat(web): downloads page with OS detection + wire account download card' (#158) from feat/downloads-page into main
Deploy marketing to Cloud Run / deploy (push) Failing after 11m4s
2026-08-16 13:17:55 +00:00
will.anderson a7d4648f1d Merge pull request 'Scale soul-demo-stage to zero when idle' (#164) from cost-opt/soul-demo-stage-minscale into main 2026-08-03 19:15:48 +00:00
will.anderson c949973632 Scale soul-demo-stage to zero when idle
soul-demo-stage is a demo/preview service pinned to min-instances=1, keeping an
instance warm 24/7. A demo doesn't need warming — set min-instances to 0 so it
scales to zero when idle (cold start on first request). The live service is
updated to match.
2026-08-03 14:10:17 -05:00
will.anderson bb419dc98c Correct safety section to claim only what the product does
The Safety section described crisis capabilities the product does not have.
Grounded against products/neuron/safety.el and the desktop safety flow, the
shipped behavior is: on-device keyword detection that surfaces crisis
resources (988, hotlines) to the user, augments the assistant's reply with
warmth and resources, uses abuse-aware guidance that never points a victim at
their own contacts, stores a Hard Bell contact locally (never uploaded), and
logs bell events on-device only. Nothing is transmitted; no one is contacted.

Corrected the false claims:
- "content routes to emergency services and crisis lines" -> it surfaces
  crisis resources to the user (card: Emergency routing -> Crisis resources)
- "emergency services or relevant authorities may be contacted ... cannot opt
  out" -> no auto-reporting exists; card now states on-device, never
  transmitted (Mandatory reporting -> On your device)
- "that contact is reached directly" / "the contact is made by your local
  instance" -> the contact is stored locally; there is no dispatch mechanism
- "I'm also establishing a Neuron crisis line - free, 24/7, staffed" -> no
  such service exists; removed
- reframed the "routing" copy that justified the above to the real
  abuse-aware guidance the HHS stat actually supports

Source only. Site build/deploy is held; dist and index.html are regenerated
at build time and are unchanged here.
2026-07-21 09:50:12 -05:00
will.anderson da5d498de5 feat(web): add downloads page and wire account download card
The funnel dead-ended at "we'll email you" — there was no downloads
surface. Add a real /downloads page in the landing template style
(shared nav + footer + page shell) with client-side navigator.platform
OS detection. macOS (Apple Silicon .dmg) is featured as the shipping
build; Windows (.msi) and Linux (.deb/.AppImage) are linked but clearly
marked "coming soon" to match current product state — Windows chat and
the Linux soul backend are not yet ready, so presenting them as fully
working would overpromise.

Also un-break the account dashboard download card, which was hardcoded
disabled ("Shipping within 30 days"). It now links the live macOS
installer plus the full /downloads page.
2026-07-14 12:18:11 -05:00
will.anderson b87b0bed24 Merge pull request 'fix(web): soften two unbacked claims for closed-beta honesty' (#157) from fix/honest-claims-closed-beta into main
Deploy marketing to Cloud Run / deploy (push) Failing after 3m33s
2026-06-18 16:40:41 +00:00
Tim Lingo ef352d2b8a fix(web): soften two false claims for closed-beta honesty
Audit found two website claims with no backing implementation:
- '3 marketplace plugins included' (free tier) -> 'Plugin marketplace access
  (coming soon)'. The marketplace is not built; there are no free plugins.
- 'Connectors - day one' -> 'Connectors (rolling out)'. Only a few connectors
  are available; Slack is coming-soon, Gmail/Drive not yet shipped.

String-content only; elc syntax-checks clean. Three other audit flags (no-card
wording, crisis-line copy, post-quantum 'everything encrypted') intentionally
NOT touched - left for human review (sensitive / conflicting evidence).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 00:42:51 -05:00
will.anderson bb690c4690 Merge pull request 'Deploy: clean prod build — strip CGI content' (#112) from stage into main
Deploy marketing to Cloud Run / deploy (push) Failing after 41s
2026-05-11 21:49:47 +00:00
will.anderson e148e6987d Merge pull request 'Force full El rebuild — strip CGI content' (#111) from dev into stage
Stage — Build, push & deploy to marketing-stage / deploy-stage (push) Successful in 7m44s
2026-05-11 21:43:20 +00:00
will.anderson 5812cb0452 Merge pull request 'Force full El rebuild — strip CGI content from base image' (#110) from fix/force-full-rebuild into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m13s
2026-05-11 21:43:09 +00:00
will.anderson c99923da1b Force full El rebuild — strip CGI content from base image
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m39s
2026-05-11 16:42:41 -05:00
will.anderson 5885826316 Merge pull request 'Deploy: CI fixes (migrations, source guard, asset-only detection)' (#109) from stage into main
Deploy marketing to Cloud Run / deploy (push) Successful in 1m47s
2026-05-11 20:59:04 +00:00
will.anderson 9554430b7e Merge pull request 'Also skip El rebuild for workflow-only changes' (#108) from dev into stage
Stage — Build, push & deploy to marketing-stage / deploy-stage (push) Successful in 8m23s
2026-05-11 20:47:04 +00:00
will.anderson 4e35cbe841 Merge pull request 'Also skip El rebuild for workflow-only changes' (#107) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m52s
2026-05-11 20:46:51 +00:00
will.anderson 62385b53c2 Also skip El rebuild for .gitea/ workflow-only changes
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m16s
Workflow file changes don't require rebuilding the El binary. Without
this, merging workflow fixes to main triggers a full El build which
hits a codegen issue in the CI version of elb.
2026-05-11 15:46:37 -05:00
will.anderson 9685a42c7d Merge pull request 'Skip El rebuild for migration/script/test-only changes' (#106) from dev into stage
Stage — Build, push & deploy to marketing-stage / deploy-stage (push) Successful in 11m48s
2026-05-11 20:45:32 +00:00
will.anderson 952b03737b Merge pull request 'Skip El rebuild for migration/script/test-only changes' (#105) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m54s
2026-05-11 20:45:14 +00:00
will.anderson d2628ec42e Skip El rebuild for migration/script/test-only changes
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m17s
migrations/, scripts/, tests/ changes don't require rebuilding the El
binary. Classifying them as asset-only avoids spurious full builds that
regenerate dist/*.c and can hit codegen incompatibilities.
2026-05-11 15:44:59 -05:00
will.anderson cac986c5e1 Merge pull request 'promote: stage → main' (#5) from stage into main
Deploy marketing to Cloud Run / deploy (push) Failing after 30s
2026-05-11 20:31:39 +00:00
will.anderson 9650dad951 Merge pull request 'Update CORS test: no-Origin requests are allowed' (#104) from dev into stage
Stage — Build, push & deploy to marketing-stage / deploy-stage (push) Successful in 8m44s
2026-05-11 20:22:39 +00:00
will.anderson d598fb7b10 Merge pull request 'Update CORS test: no-Origin requests are allowed' (#103) from fix/stage-ci-paths into dev 2026-05-11 20:22:30 +00:00
will.anderson 1eeb8df04b Update CORS test: no-Origin requests are allowed (same-origin fix)
Same-origin browser fetches don't send Origin. The server correctly
allows them — blocking was the bug that broke checkout. Update the
test to match the fixed behavior.
2026-05-11 15:22:22 -05:00
will.anderson c3aec8947a Merge pull request 'Fix stage source guard: fetch origin/dev before ancestry check' (#102) from dev into stage
Stage — Build, push & deploy to marketing-stage / deploy-stage (push) Failing after 9m28s
2026-05-11 19:09:52 +00:00
will.anderson 9e5d7e55ab Merge pull request 'Fix stage source guard: fetch origin/dev before ancestry check' (#101) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m48s
2026-05-11 19:09:33 +00:00
will.anderson 5d3b1a3e20 Fix stage source guard: fetch origin/dev before ancestry check
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m22s
The shallow clone (fetch-depth: 2) doesn't include origin/dev, so
git merge-base --is-ancestor was silently failing. Fetch dev with
depth=1 first so custom merge commit titles still pass the check.
2026-05-11 14:09:18 -05:00
will.anderson cd93af38fb Merge pull request 'promote: stage → main' (#5) from stage into main 2026-05-05 11:07:25 +00:00
11 changed files with 196 additions and 29 deletions
+3 -2
View File
@@ -53,8 +53,9 @@ jobs:
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD 2>/dev/null || echo "unknown")
echo "Changed files:"
echo "$CHANGED"
# Asset-only: only src/assets/, src/shares/, src/index.html, src/about.html, src/terms.html, src/enterprise-terms.html, src/llms.txt
NON_ASSET=$(echo "$CHANGED" | grep -v '^src/assets/' | grep -v '^src/shares/' | grep -v '^src/index\.html' | grep -v '^src/about\.html' | grep -v '^src/terms\.html' | grep -v '^src/enterprise-terms\.html' | grep -v '^src/llms\.txt' | grep -v '^$' || true)
# Asset-only: files that don't require rebuilding the El binary.
# migrations/, scripts/, tests/ are data/infra/test changes — no binary rebuild needed.
NON_ASSET=$(echo "$CHANGED" | grep -v '^src/assets/' | grep -v '^src/shares/' | grep -v '^src/index\.html' | grep -v '^src/about\.html' | grep -v '^src/terms\.html' | grep -v '^src/enterprise-terms\.html' | grep -v '^src/llms\.txt' | grep -v '^migrations/' | grep -v '^scripts/' | grep -v '^tests/' | grep -v '^\.gitea/' | grep -v '^$' || true)
if [ -z "$NON_ASSET" ] && [ "$CHANGED" != "unknown" ]; then
echo "asset_only=true" >> "$GITHUB_OUTPUT"
echo "=> Asset-only change detected, will use fast path"
+4 -2
View File
@@ -52,6 +52,8 @@ jobs:
set -euo pipefail
COMMIT_MSG=$(git log -1 --pretty=format:"%s" 2>/dev/null || true)
echo "Merge commit: $COMMIT_MSG"
# Fetch dev so ancestry check works in the shallow clone.
git fetch --depth=1 origin dev 2>/dev/null || true
# Gitea merge commits: "Merge pull request '...' (#N) from dev into stage"
# Direct branch merges: "Merge branch 'dev' into stage"
# tea pr merge with custom title: any subject line is possible, so
@@ -79,7 +81,7 @@ jobs:
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD 2>/dev/null || echo "unknown")
echo "Changed files:"
echo "$CHANGED"
NON_ASSET=$(echo "$CHANGED" | grep -v '^src/assets/' | grep -v '^src/shares/' | grep -v '^src/index\.html' | grep -v '^src/about\.html' | grep -v '^src/terms\.html' | grep -v '^src/enterprise-terms\.html' | grep -v '^src/llms\.txt' | grep -v '^$' || true)
NON_ASSET=$(echo "$CHANGED" | grep -v '^src/assets/' | grep -v '^src/shares/' | grep -v '^src/index\.html' | grep -v '^src/about\.html' | grep -v '^src/terms\.html' | grep -v '^src/enterprise-terms\.html' | grep -v '^src/llms\.txt' | grep -v '^migrations/' | grep -v '^scripts/' | grep -v '^tests/' | grep -v '^\.gitea/' | grep -v '^$' || true)
if [ -z "$NON_ASSET" ] && [ "$CHANGED" != "unknown" ]; then
echo "asset_only=true" >> "$GITHUB_OUTPUT"
echo "=> Asset-only change detected, will use fast path"
@@ -271,7 +273,7 @@ jobs:
--service-account neuron-marketing-sa@neuron-785695.iam.gserviceaccount.com \
--update-env-vars "NEURON_LLM_0_FORMAT=anthropic,NEURON_LLM_0_MODEL=claude-sonnet-4-5,NEURON_LLM_0_URL=https://api.anthropic.com/v1/messages" \
--update-secrets "NEURON_LLM_0_KEY=anthropic-api-key:latest,ANTHROPIC_API_KEY=anthropic-api-key:latest" \
--min-instances 1 \
--min-instances 0 \
--max-instances 50 \
--concurrency 20 \
--port 8080 \
+11 -5
View File
@@ -800,11 +800,17 @@ fn account_download_card() -> String {
el_div(
"class=\"card-dark\" style=\"border-top:3px solid var(--navy)\"",
el_p("class=\"card-label\"", "Download") +
el_p("class=\"download-status\" style=\"color:var(--navy)\"", "Shipping within 30 days") +
el_p("class=\"download-title\"", "Neuron for Mac &amp; Windows") +
el_p("class=\"download-body\"", "macOS and Windows simultaneously. You&#39;ll receive a download link and license key by email the moment it ships. Nothing to do right now.") +
el_button("type=\"button\" class=\"download-btn-disabled\" disabled aria-disabled=\"true\"",
account_signin_svg_download() + " Download arriving soon"
el_p("class=\"download-status\" style=\"color:var(--navy)\"", "Available now &mdash; macOS") +
el_p("class=\"download-title\"", "Neuron for Mac") +
el_p("class=\"download-body\"", "The macOS build (Apple Silicon) is ready to download now. Windows and Linux are coming soon. Your license key is in your launch email.") +
el_div(
"style=\"display:flex;gap:.75rem;flex-wrap:wrap;align-items:center\"",
el_a(
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-macos-arm64.dmg",
"class=\"btn-primary\" download",
account_signin_svg_download() + " Download for macOS"
) +
el_a("/downloads", "class=\"btn-ghost\"", "All platforms &#8594;")
)
)
}
+1 -1
View File
@@ -98,7 +98,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
el_li("", "Persistent memory - never resets")
+ el_li("", "Local inference via Ollama (coming)")
+ el_li("", "Bring your own API keys")
+ el_li("", "3 marketplace plugins included")
+ el_li("", "Plugin marketplace access (coming soon)")
+ el_li("", "Core built-in capabilities")
+ el_li("", "2 devices included")
} else {
+148
View File
@@ -0,0 +1,148 @@
// components/downloads.el - Public downloads page.
//
// Landing-styled inner page (page_open_seo + nav + content + footer +
// page_close, assembled by main.el). Client-side OS detection via
// navigator.platform highlights the visitor's platform and updates the
// detected-OS banner. macOS is the shipping build; Windows and Linux are
// linked but clearly marked "coming soon" per the current product state
// (Windows chat not yet enabled, Linux soul backend not yet complete).
from nav import { nav }
from footer import { footer }
extern fn el_section(attrs: String, children: String) -> String
extern fn el_div(attrs: String, children: String) -> String
extern fn el_span(attrs: String, children: String) -> String
extern fn el_h1(attrs: String, text: String) -> String
extern fn el_p(attrs: String, children: String) -> String
extern fn el_a(href: String, attrs: String, children: String) -> String
// Scoped presentation. Uses the landing stylesheet's CSS variables
// (--navy, --head, --body, --t2, --t3) so it inherits the site palette.
// No # colors or quoted font names inline routed through vars to stay
// clear of the elc CSS tokenizer edge cases.
fn dl_style() -> String {
"<style>" +
".dl-wrap{max-width:960px}" +
".dl-detected{font-size:.85rem;font-weight:500;color:var(--navy);margin:1.5rem 0 2.5rem;min-height:1.2em}" +
".dl-card{padding:2rem}" +
".dl-featured{border-top:3px solid var(--navy)}" +
".dl-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;margin-top:1.5rem}" +
"@media (max-width:720px){.dl-grid{grid-template-columns:1fr}}" +
".dl-badge{display:inline-block;font-size:.6rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;padding:.35rem .7rem;border-radius:999px;margin-bottom:1rem}" +
".dl-badge-ready{color:var(--navy);background:rgba(0,82,160,.10)}" +
".dl-badge-soon{color:var(--t3);background:rgba(0,0,0,.05)}" +
".dl-title{font-family:var(--head);font-size:1.4rem;font-weight:600;margin-bottom:.4rem}" +
".dl-sub{font-family:var(--body);font-weight:300;font-size:.9rem;color:var(--t2);line-height:1.6;margin-bottom:1.25rem}" +
".dl-note{font-size:.78rem;color:var(--t3);line-height:1.6;margin-top:1rem}" +
".dl-btnrow{display:flex;gap:.75rem;flex-wrap:wrap;align-items:center}" +
"[data-detected=true]{box-shadow:0 0 0 2px rgba(0,82,160,.45);border-radius:2px}" +
"</style>"
}
fn dl_header() -> String {
el_p("class=\"label reveal\"", "Download") +
el_h1("class=\"display-lg reveal\" style=\"margin-top:.75rem\"", "Get Neuron.") +
el_div("class=\"navy-line-left reveal\" style=\"width:4rem;margin:1.5rem 0 2rem\"", "") +
el_p(
"class=\"dl-sub reveal\" style=\"max-width:34rem;font-size:1rem\"",
"Your AI that remembers you, running on your own machine. macOS is available to download today. Windows and Linux are on the way."
) +
el_p("id=\"os-detected\" class=\"dl-detected reveal\"", "Detecting your platform&#8230;")
}
fn dl_mac_card() -> String {
el_div(
"id=\"card-mac\" class=\"card-dark dl-card dl-featured reveal\"",
el_span("class=\"dl-badge dl-badge-ready\"", "Available now") +
el_p("class=\"dl-title\"", "Neuron for macOS") +
el_p("class=\"dl-sub\"", "Apple Silicon (M1&#8211;M4). macOS 13 Ventura or later.") +
el_div(
"class=\"dl-btnrow\"",
el_a(
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-macos-arm64.dmg",
"class=\"btn-primary\" download",
"Download for macOS &#8594;"
)
) +
el_p("class=\"dl-note\"", "Version 1.1.0 &middot; Apple Silicon &middot; .dmg")
)
}
fn dl_windows_card() -> String {
el_div(
"id=\"card-windows\" class=\"card-dark dl-card reveal\"",
el_span("class=\"dl-badge dl-badge-soon\"", "Coming soon") +
el_p("class=\"dl-title\"", "Neuron for Windows") +
el_p("class=\"dl-sub\"", "Windows 10 and 11 (x64).") +
el_div(
"class=\"dl-btnrow\"",
el_a(
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-windows-x64.msi",
"class=\"btn-ghost\" download",
"Download .msi"
)
) +
el_p(
"class=\"dl-note\"",
"The Windows build is still landing &mdash; this link may not be live yet. Chat is not enabled on Windows in this release."
)
)
}
fn dl_linux_card() -> String {
el_div(
"id=\"card-linux\" class=\"card-dark dl-card reveal\"",
el_span("class=\"dl-badge dl-badge-soon\"", "Coming soon") +
el_p("class=\"dl-title\"", "Neuron for Linux") +
el_p("class=\"dl-sub\"", "Debian / Ubuntu (.deb) or portable (.AppImage), x64.") +
el_div(
"class=\"dl-btnrow\"",
el_a(
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-linux-x64.deb",
"class=\"btn-ghost\" download",
"Download .deb"
) +
el_a(
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-linux-x64.AppImage",
"class=\"btn-ghost\" download",
"Download .AppImage"
)
) +
el_p(
"class=\"dl-note\"",
"Linux packages install today, but the on-device soul backend is still being finished. Full support is coming soon."
)
)
}
fn dl_fineprint() -> String {
el_p(
"class=\"dl-note reveal\" style=\"margin-top:2.5rem\"",
"All installers are served from downloads.neurontechnologies.ai. After checkout your license key arrives by email &mdash; manage it from your " +
el_a("/account", "style=\"color:var(--navy)\"", "account") +
"."
)
}
// Inline OS detection. CSP allows script-src 'unsafe-inline'. Single-quoted
// JS, ASCII only, no // comments keeps the .el string simple and avoids
// tokenizer surprises.
fn dl_script() -> String {
"<script>(function(){var p=(navigator.platform||'')+' '+(navigator.userAgent||'');var os='mac';if(/Win/i.test(p)){os='windows';}else if(/Linux|X11/i.test(p)&&!/Android/i.test(p)){os='linux';}else if(/Mac/i.test(p)){os='mac';}var L={mac:'macOS',windows:'Windows',linux:'Linux'};var b=document.getElementById('os-detected');if(b){b.textContent=(os==='mac')?'We detected macOS on this device. You are ready to download.':('We detected '+L[os]+'. '+L[os]+' support is coming soon; macOS is available to download today.');}var c=document.getElementById('card-'+os);if(c){c.setAttribute('data-detected','true');}})();</script>"
}
fn downloads_content() -> String {
let grid: String = el_div("class=\"dl-grid\"", dl_windows_card() + dl_linux_card())
el_section(
"id=\"downloads\" aria-label=\"Download Neuron\"",
el_div(
"class=\"container dl-wrap\"",
dl_header() + dl_mac_card() + grid + dl_fineprint()
)
)
}
fn downloads_page() -> String {
return nav() + dl_style() + downloads_content() + footer() + dl_script()
}
+1
View File
@@ -1,4 +1,5 @@
// components/hero.el - Hero section.
// Rebuilt: 2026-05-11.
//
// Full-bleed hero with headline, sub-copy, and two CTAs.
// Glow orbs are pure CSS absolute-positioned divs.
+10
View File
@@ -87,6 +87,7 @@ from checkout import { checkout_page }
from safety import { safety }
from gallery import { gallery_page }
from account import { account_page }
from downloads import { downloads_page }
// Share-card HTML allowlist
//
@@ -1971,6 +1972,15 @@ fn handle_request_inner(method: String, path: String, headers: Map, body: String
) + badge_css + success_body + page_close()
}
// Downloads
// Public installer page. macOS ships today; Windows/Linux linked but marked
// coming soon. Live-rendered like /about (page shell wraps downloads_page()).
if str_starts_with(path, "/downloads") {
let dl_title: String = "Download Neuron - macOS, Windows & Linux"
let dl_desc: String = "Download Neuron for macOS. Your AI that remembers you, running on your own machine. Windows and Linux support is on the way."
return page_open_seo(dl_title, dl_desc, "/downloads", dl_desc, "false") + downloads_page() + page_close()
}
// Account dashboard
// Use prefix match so OAuth/email-confirmation redirects with query strings still hit.
if str_starts_with(path, "/account") {
+1 -1
View File
@@ -77,7 +77,7 @@ fn marketplace_categories() -> String {
el_div(
"class=\"marketplace-categories reveal\" style=\"transition-delay:320ms\"",
marketplace_tags_block("Connectors - day one", connectors) +
marketplace_tags_block("Connectors (rolling out)", connectors) +
marketplace_tags_block("Following launch", following) +
el_div(
"",
+1 -1
View File
@@ -36,7 +36,7 @@ fn pricing_free_features() -> String {
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Local inference via Ollama (coming)")) +
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Neuron Inference included when it launches - Q3 2026")) +
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Unlimited projects")) +
el_li("", el_span("class=\"dash\"", "-") + el_span("", "3 marketplace plugins included")) +
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Plugin marketplace access (coming soon)")) +
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Core built-in capabilities"))
}
+10 -13
View File
@@ -47,16 +47,16 @@ fn safety_cards() -> String {
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "Hard Bell") +
el_p("style=\"font-family:var(--body);font-weight:400;font-size:0.9375rem;color:var(--t1);margin-bottom:0.5rem\"", "A trusted contact the threat can&#39;t intercept") +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.875rem;color:var(--t2);line-height:1.7\"",
"Any user can designate a Hard Bell contact - a friend, a relative, a colleague - set up in a calm moment, independent of any shared account. When a signal warrants it, that contact is reached directly. It works the same way for everyone: adult, teen, or child."
"Any user can designate a Hard Bell contact - a friend, a relative, a colleague - set up in a calm moment, independent of any shared account. It&#39;s stored on your device only, never uploaded to our servers. It works the same way for everyone: adult, teen, or child."
)
)
let card2: String = el_div(
"class=\"reveal card-dark\" style=\"transition-delay:150ms;" + card_style + "\"",
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "Emergency routing") +
el_p("style=\"font-family:var(--body);font-weight:400;font-size:0.9375rem;color:var(--t1);margin-bottom:0.5rem\"", "Emergency services first - not notification") +
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "Crisis resources") +
el_p("style=\"font-family:var(--body);font-weight:400;font-size:0.9375rem;color:var(--t1);margin-bottom:0.5rem\"", "The help surfaces first - not a notification") +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.875rem;color:var(--t2);line-height:1.7\"",
"Physical danger and crisis signals route to emergency services and crisis lines. Neuron evaluates the content of the signal, not the account type. No one in your contact list can disable or redirect this path."
"When Neuron detects a physical-danger or crisis signal, it surfaces crisis-line and emergency resources to you directly - 988, the domestic-violence hotline, Crisis Text Line. It evaluates the content of the signal, not the account type. No one in your contact list can disable or redirect this."
)
)
@@ -74,10 +74,10 @@ fn safety_cards() -> String {
let card4: String = el_div(
"class=\"reveal card-dark\" style=\"transition-delay:250ms;" + card_style + "\"",
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "Mandatory reporting") +
el_p("style=\"font-family:var(--body);font-weight:400;font-size:0.9375rem;color:var(--t1);margin-bottom:0.5rem\"", "If real harm is intended, authorities may be contacted") +
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "On your device") +
el_p("style=\"font-family:var(--body);font-weight:400;font-size:0.9375rem;color:var(--t1);margin-bottom:0.5rem\"", "Detection never leaves the machine") +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.875rem;color:var(--t2);line-height:1.7\"",
"If Neuron detects credible, specific indicators of intended harm - to the user or to someone else - emergency services or relevant authorities may be contacted. Every user agrees to this at account creation. You cannot opt out."
"Crisis detection runs locally, on device, with zero added latency. Triggers are logged on your machine only and never transmitted. Neuron, LLC never sees the signal, the conversation, or that a bell fired."
)
)
@@ -90,7 +90,7 @@ fn safety_statements() -> String {
let people_first: String = el_div(
"class=\"reveal card-dark\" style=\"padding:2rem 2.5rem;border-left:3px solid var(--navy);margin-bottom:1.5rem\"",
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8;margin-bottom:1.25rem\"",
"This applies to anyone planning mass harm, a shooting, or serious self-harm. Not just child protection - anyone. The same logic holds: a misunderstanding can be resolved. A person&#39;s life can&#39;t be given back to them."
"This holds for anyone, not just children. When something sounds like serious self-harm or danger, Neuron stops and leads with help - it acknowledges what it heard, tells you it&#39;s concerned, and puts crisis resources in front of you instead of just answering the surface question."
) +
el_p("style=\"font-family:var(--body);font-weight:700;font-size:1.125rem;color:var(--navy);line-height:1.4\"",
"People first, always."
@@ -99,7 +99,7 @@ fn safety_statements() -> String {
let stat_card1: String = el_div(
"class=\"reveal card-dark\" style=\"padding:2rem 2.5rem;border-left:3px solid rgba(0,82,160,.30)\"",
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "Why the routing works this way") +
el_p("style=\"font-family:var(--body);font-size:0.75rem;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--navy);margin-bottom:0.75rem\"", "Why we never point you at your own contacts") +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8;margin-bottom:0.75rem\"",
"According to federal child maltreatment data (HHS, Child Maltreatment 2023), approximately 89% of child abuse victims are maltreated by a parent or caregiver. The emergency contact on file is not always a safe contact."
) +
@@ -117,7 +117,7 @@ fn safety_statements() -> String {
"I recognize I will lose business over this. That doesn&#39;t matter to me."
) +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8\"",
"This is still local. Neuron, LLC does not see your conversations. When a safety signal fires, the contact is made by your local instance - not by our servers. We are not in the loop. We cannot be. That&#39;s the architecture."
"This is still local. Neuron, LLC does not see your conversations. When a safety signal fires, everything happens on your device - the help is surfaced to you locally. Our servers are never in the loop. We cannot be. That&#39;s the architecture."
)
)
@@ -133,9 +133,6 @@ fn safety_statements() -> String {
) +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8;margin-bottom:1rem\"",
"If you don&#39;t have someone to designate, you can use 988 - the Suicide &amp; Crisis Lifeline - as your Hard Bell contact. The system will accept it. The point is that no one goes in without a line out."
) +
el_p("style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8\"",
"I&#39;m also establishing a Neuron crisis line - free, 24/7, staffed. Because I recognize that some people don&#39;t have anyone. The technology shouldn&#39;t make that worse. It should be the thing that catches you when nothing else does."
)
)
+6 -4
View File
@@ -51,11 +51,13 @@ test.describe('Security headers', () => {
// - anything else (e.g. evil.com): BLOCKED (403)
test.describe('CORS enforcement — /api/supabase-config', () => {
test('Rejects requests with no Origin header', async () => {
// No Origin = not from a browser context — the server treats this as
// an unknown caller and returns 403 to prevent server-side exfiltration.
test('Allows requests with no Origin header (same-origin browser fetches)', async () => {
// Same-origin browser fetches (e.g. checkout page fetching supabase-config on
// the same domain) do not send an Origin header. The server must pass these
// through — blocking them would break the checkout flow on production.
// Server-side exfiltration is prevented by the evil-origin 403 below.
const r = await get('/api/supabase-config');
expect(r.status).toBe(403);
expect(r.status).toBe(200);
});
test('Rejects evil origin', async () => {