security: pentest fixes — deploy to stage #70
Vendored
+16
-3
@@ -10,9 +10,18 @@ el_val_t page_schema(void) {
|
||||
" \"@context\": \"https://schema.org\",\n"
|
||||
" \"@graph\": [\n"
|
||||
" {\n"
|
||||
" \"@type\": \"WebSite\",\n"
|
||||
" \"name\": \"Neuron\",\n"
|
||||
" \"url\": \"https://neurontechnologies.ai\"\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"@type\": \"Organization\",\n"
|
||||
" \"name\": \"Neuron, LLC\",\n"
|
||||
" \"url\": \"https://neurontechnologies.ai\",\n"
|
||||
" \"logo\": {\n"
|
||||
" \"@type\": \"ImageObject\",\n"
|
||||
" \"url\": \"https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png\"\n"
|
||||
" },\n"
|
||||
" \"founder\": {\n"
|
||||
" \"@type\": \"Person\",\n"
|
||||
" \"name\": \"Will Anderson\",\n"
|
||||
@@ -20,11 +29,15 @@ el_val_t page_schema(void) {
|
||||
" },\n"
|
||||
" \"description\": \"Neuron builds AI that runs on your machine, builds a memory over time, and gets sharper the longer you use it. One builder. Built different.\",\n"
|
||||
" \"foundingDate\": \"2026\",\n"
|
||||
" \"sameAs\": [\"https://github.com/neuron-technologies\"]\n"
|
||||
" \"sameAs\": [\n"
|
||||
" \"https://github.com/neuron-technologies\",\n"
|
||||
" \"https://x.com/neurontechai\"\n"
|
||||
" ]\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"@type\": \"SoftwareApplication\",\n"
|
||||
" \"name\": \"Neuron\",\n"
|
||||
" \"url\": \"https://neurontechnologies.ai\",\n"
|
||||
" \"applicationCategory\": \"AIApplication\",\n"
|
||||
" \"operatingSystem\": \"macOS, Windows, Linux\",\n"
|
||||
" \"offers\": [\n"
|
||||
@@ -39,7 +52,7 @@ el_val_t page_schema(void) {
|
||||
" \"name\": \"Professional\",\n"
|
||||
" \"price\": \"19\",\n"
|
||||
" \"priceCurrency\": \"USD\",\n"
|
||||
" \"billingIncrement\": \"monthly\"\n"
|
||||
" \"billingPeriod\": \"P1M\"\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"@type\": \"Offer\",\n"
|
||||
@@ -67,7 +80,7 @@ el_val_t page_schema(void) {
|
||||
" \"name\": \"What is Neuron?\",\n"
|
||||
" \"acceptedAnswer\": {\n"
|
||||
" \"@type\": \"Answer\",\n"
|
||||
" \"text\": \"Neuron is an AI that runs on your machine and builds a persistent memory over time. Every other AI forgets you when you close the tab. Neuron doesn't. The longer you use it, the less you have to explain.\"\n"
|
||||
" \"text\": \"Neuron is an AI that runs on your machine and builds a persistent memory over time. Every other AI forgets you when you close the tab. Neuron doesn't. The longer you use it, the less you have to explain.\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// components/enterprise_terms.el - Enterprise Agreement page.
|
||||
// Returns complete HTML using the shared page shell from styles.el.
|
||||
|
||||
from styles import { page_open, page_close }
|
||||
from styles import { page_open_seo, page_close }
|
||||
from nav import { nav }
|
||||
|
||||
extern fn el_div(attrs: String, children: String) -> String
|
||||
@@ -15,7 +15,13 @@ extern fn el_li(attrs: String, children: String) -> String
|
||||
extern fn el_em(children: String) -> String
|
||||
|
||||
fn enterprise_terms_page() -> String {
|
||||
page_open() + enterprise_terms_body() + page_close()
|
||||
page_open_seo(
|
||||
"Enterprise Agreement — Neuron",
|
||||
"The Neuron Enterprise Agreement governs enterprise deployments of Neuron software. Review licensing terms, data handling, and compliance provisions.",
|
||||
"/legal/enterprise-terms",
|
||||
"The Neuron Enterprise Agreement — governing enterprise deployments, licensing, data handling, and compliance.",
|
||||
"false"
|
||||
) + enterprise_terms_body() + page_close()
|
||||
}
|
||||
|
||||
fn et_section(num: String, title: String, body: String) -> String {
|
||||
|
||||
+127
-13
@@ -78,7 +78,7 @@ from pricing import { pricing }
|
||||
from marketplace import { marketplace }
|
||||
from viral import { viral }
|
||||
from footer import { footer }
|
||||
from styles import { page_open, page_close }
|
||||
from styles import { page_open, page_open_seo, page_close }
|
||||
from about import { about_page }
|
||||
from founding_badge import { founding_badge, founding_badge_css }
|
||||
from terms import { terms_page }
|
||||
@@ -224,7 +224,7 @@ fn share_card_page(question: String, answer_plain: String, answer_html_in: Strin
|
||||
// Use plaintext for og:description so social previews are readable.
|
||||
let answer: String = answer_plain
|
||||
let og_desc: String = str_slice(answer, 0, 140)
|
||||
let base_url: String = state_get("__neuron_origin__")
|
||||
let base_url: String = state_get("__origin__")
|
||||
let card_url: String = base_url + "/share/" + id
|
||||
// Pre-built share hrefs — ID is digits so no URL encoding needed
|
||||
let share_text: String = "The+AI+that+remembers+you+%E2%80%94+things+it+said%3A"
|
||||
@@ -554,7 +554,7 @@ fn config_get(key: String) -> String {
|
||||
// function - it serves __html_file__ directly with text/html.
|
||||
// This handler covers /api/* and /brand/* routes.
|
||||
|
||||
fn handle_request_inner(method: String, path: String, body: String) -> String {
|
||||
fn handle_request_inner(method: String, path: String, headers: Map, body: String) -> String {
|
||||
let src_dir: String = state_get("__src_dir__")
|
||||
|
||||
// ── Root — serve El-generated landing page ────────────────────────────────
|
||||
@@ -572,7 +572,19 @@ fn handle_request_inner(method: String, path: String, body: String) -> String {
|
||||
|
||||
// ── robots.txt ────────────────────────────────────────────────────────────
|
||||
if str_eq(path, "/robots.txt") {
|
||||
return "User-agent: *\nAllow: /\n"
|
||||
return "User-agent: *\nAllow: /\nDisallow: /checkout\nDisallow: /account\nDisallow: /api/\nSitemap: https://neurontechnologies.ai/sitemap.xml\n"
|
||||
}
|
||||
|
||||
// ── sitemap.xml ───────────────────────────────────────────────────────────
|
||||
if str_eq(path, "/sitemap.xml") {
|
||||
let sitemap_body: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"
|
||||
+ " <url><loc>https://neurontechnologies.ai/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>\n"
|
||||
+ " <url><loc>https://neurontechnologies.ai/about</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>\n"
|
||||
+ " <url><loc>https://neurontechnologies.ai/legal/terms</loc><changefreq>monthly</changefreq><priority>0.3</priority></url>\n"
|
||||
+ " <url><loc>https://neurontechnologies.ai/legal/enterprise-terms</loc><changefreq>monthly</changefreq><priority>0.3</priority></url>\n"
|
||||
+ "</urlset>\n"
|
||||
return http_response(200, "{\"Content-Type\":\"application/xml; charset=utf-8\"}", sitemap_body)
|
||||
}
|
||||
|
||||
// ── About page ────────────────────────────────────────────────────────────
|
||||
@@ -612,7 +624,25 @@ fn handle_request_inner(method: String, path: String, body: String) -> String {
|
||||
plan = "free"
|
||||
}
|
||||
let pub_key: String = state_get("__stripe_publishable_key__")
|
||||
return page_open() + checkout_page(plan, pub_key) + page_close()
|
||||
let checkout_title: String = if str_eq(plan, "founding") {
|
||||
"Founding Member Checkout — Neuron"
|
||||
} else {
|
||||
if str_eq(plan, "free") {
|
||||
"Get Started Free — Neuron"
|
||||
} else {
|
||||
"Professional Plan Checkout — Neuron"
|
||||
}
|
||||
}
|
||||
let checkout_desc: String = if str_eq(plan, "founding") {
|
||||
"Secure your Founding Member spot. Pay once, $199 lifetime — Neuron inference included at launch, priced below the major APIs. First 1,000 only."
|
||||
} else {
|
||||
if str_eq(plan, "free") {
|
||||
"Create your free Neuron account. No credit card required. Your AI that remembers you — runs on your machine, never resets."
|
||||
} else {
|
||||
"Subscribe to Neuron Professional for $19/month. The AI that remembers you — persistent memory, runs locally, bring your own API keys."
|
||||
}
|
||||
}
|
||||
return page_open_seo(checkout_title, checkout_desc, "/checkout", checkout_desc, "true") + checkout_page(plan, pub_key) + page_close()
|
||||
}
|
||||
|
||||
// ── Stripe payment intent / setup intent ─────────────────────────────────
|
||||
@@ -1116,14 +1146,33 @@ fn handle_request_inner(method: String, path: String, body: String) -> String {
|
||||
}
|
||||
|
||||
// ── Supabase public config ────────────────────────────────────────────────
|
||||
// CORS-gated: only requests from neurontechnologies.ai origins or localhost
|
||||
// may fetch the anon key. Restricting this reduces the blast radius of any
|
||||
// future Supabase RLS misconfiguration — an attacker on an arbitrary origin
|
||||
// would not be able to silently obtain the key to make authenticated calls.
|
||||
if str_eq(path, "/api/supabase-config") {
|
||||
let req_origin: String = map_get(headers, "origin")
|
||||
let origin_ok: Bool = str_eq(req_origin, "")
|
||||
|| str_eq(req_origin, "https://neurontechnologies.ai")
|
||||
|| str_eq(req_origin, "https://www.neurontechnologies.ai")
|
||||
|| str_starts_with(req_origin, "http://localhost:")
|
||||
|| str_starts_with(req_origin, "http://127.0.0.1:")
|
||||
if !origin_ok {
|
||||
return "{\"__status__\":403,\"error\":\"forbidden\"}"
|
||||
}
|
||||
let proj_url: String = "https://ocojsghaonltunidkzpw.supabase.co"
|
||||
let anon_key: String = state_get("__supabase_anon_key__")
|
||||
return "{\"url\":\"" + proj_url + "\",\"anon_key\":\"" + anon_key + "\"}"
|
||||
}
|
||||
|
||||
// ── Soul health diagnostic — surfaces raw signal from in-container soul ──
|
||||
// Requires X-Internal: true header to prevent public exposure of internal
|
||||
// service topology, soul URL, and probe responses.
|
||||
if str_eq(path, "/api/soul-health") {
|
||||
let x_internal: String = map_get(headers, "x-internal")
|
||||
if !str_eq(x_internal, "true") {
|
||||
return "{\"__status__\":404,\"error\":\"not found\"}"
|
||||
}
|
||||
if str_eq(method, "GET") {
|
||||
let soul_base: String = state_get("__soul_url__")
|
||||
// Probe 1: bare GET / — does ANYTHING listen?
|
||||
@@ -1396,7 +1445,51 @@ fn handle_request_inner(method: String, path: String, body: String) -> String {
|
||||
// magic-link invite email so the buyer can sign in and see their
|
||||
// plan on /account. Idempotent: existing users get a fresh link.
|
||||
// 4. Forwards to license API for key provisioning when configured.
|
||||
//
|
||||
// SECURITY: Stripe-Signature header is verified via HMAC-SHA256 before
|
||||
// any processing occurs. Without this check an attacker could POST a
|
||||
// forged payment_intent.succeeded event and increment the founding counter
|
||||
// or trigger account provisioning for an arbitrary email.
|
||||
//
|
||||
// Stripe signature format: "t=<timestamp>,v1=<hex_sig>[,v1=...]"
|
||||
// Signed payload: "<timestamp>.<raw_body>"
|
||||
// Key: STRIPE_WEBHOOK_SECRET (whsec_... value from Stripe dashboard)
|
||||
if str_eq(path, "/api/webhooks/stripe") {
|
||||
let wh_secret: String = state_get("__stripe_webhook_secret__")
|
||||
if !str_eq(wh_secret, "") {
|
||||
let sig_header: String = map_get(headers, "stripe-signature")
|
||||
if str_eq(sig_header, "") {
|
||||
println("[webhook] rejected: missing Stripe-Signature header")
|
||||
return "{\"__status__\":400,\"error\":\"missing signature\"}"
|
||||
}
|
||||
// Extract t= value from sig header
|
||||
let t_idx: Int = str_index_of(sig_header, "t=")
|
||||
let t_val: String = ""
|
||||
if t_idx >= 0 {
|
||||
let t_tail: String = str_slice(sig_header, t_idx + 2, str_len(sig_header))
|
||||
let t_comma: Int = str_index_of(t_tail, ",")
|
||||
let t_val = if t_comma >= 0 { str_slice(t_tail, 0, t_comma) } else { t_tail }
|
||||
}
|
||||
// Extract v1= value from sig header
|
||||
let v1_idx: Int = str_index_of(sig_header, "v1=")
|
||||
let v1_val: String = ""
|
||||
if v1_idx >= 0 {
|
||||
let v1_tail: String = str_slice(sig_header, v1_idx + 3, str_len(sig_header))
|
||||
let v1_comma: Int = str_index_of(v1_tail, ",")
|
||||
let v1_val = if v1_comma >= 0 { str_slice(v1_tail, 0, v1_comma) } else { v1_tail }
|
||||
}
|
||||
if str_eq(t_val, "") || str_eq(v1_val, "") {
|
||||
println("[webhook] rejected: malformed Stripe-Signature header")
|
||||
return "{\"__status__\":400,\"error\":\"invalid signature format\"}"
|
||||
}
|
||||
// Compute expected HMAC: HMAC-SHA256(secret, "<t_val>.<body>")
|
||||
let signed_payload: String = t_val + "." + body
|
||||
let expected_sig: String = hmac_sha256_hex(wh_secret, signed_payload)
|
||||
if !str_eq(expected_sig, v1_val) {
|
||||
println("[webhook] rejected: signature mismatch")
|
||||
return "{\"__status__\":400,\"error\":\"signature verification failed\"}"
|
||||
}
|
||||
}
|
||||
let is_session_done: Bool = str_contains(body, "checkout.session.completed")
|
||||
let is_pi_done: Bool = str_contains(body, "payment_intent.succeeded")
|
||||
let is_si_done: Bool = str_contains(body, "setup_intent.succeeded")
|
||||
@@ -1841,7 +1934,13 @@ fn handle_request_inner(method: String, path: String, body: String) -> String {
|
||||
+ el_a("/", "class=\"btn-ghost\"", "Back to home")
|
||||
)
|
||||
)
|
||||
return page_open() + badge_css + success_body + page_close()
|
||||
return page_open_seo(
|
||||
"Welcome to Neuron — Your Membership is Confirmed",
|
||||
"Your Neuron membership is confirmed. Download the app and let the AI that remembers you get to work.",
|
||||
"/marketplace/success",
|
||||
"Your Neuron membership is confirmed. Download the app and let the AI that remembers you get to work.",
|
||||
"true"
|
||||
) + badge_css + success_body + page_close()
|
||||
}
|
||||
|
||||
// ── Account dashboard ─────────────────────────────────────────────────────
|
||||
@@ -2062,21 +2161,28 @@ fn js_headers_json() -> String {
|
||||
+ "\"X-Content-Type-Options\":\"nosniff\","
|
||||
+ "\"X-Frame-Options\":\"SAMEORIGIN\","
|
||||
+ "\"Referrer-Policy\":\"strict-origin-when-cross-origin\","
|
||||
+ "\"Permissions-Policy\":\"geolocation=(), microphone=(), camera=()\"}"
|
||||
+ "\"Permissions-Policy\":\"geolocation=(), microphone=(), camera=()\","
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; frame-src https://challenges.cloudflare.com; connect-src 'self' https://api.stripe.com https://*.supabase.co; img-src 'self' data: https:; font-src 'self' data:\"}"
|
||||
}
|
||||
|
||||
// Headers for static assets under /assets/ and /brand/.
|
||||
// max-age=31536000 (1 year) + immutable tells Cloudflare to cache at the edge
|
||||
// and never revalidate — assets are versioned by filename or content so stale
|
||||
// delivery is not a risk. This eliminates Cloud Run hits for every image/font/svg.
|
||||
// Security headers are included so asset responses are equally hardened even
|
||||
// when served directly (e.g. Cloudflare bypass or direct origin fetch).
|
||||
fn static_asset_headers_json() -> String {
|
||||
"{\"Cache-Control\":\"public, max-age=31536000, immutable\","
|
||||
+ "\"Strict-Transport-Security\":\"max-age=63072000; includeSubDomains; preload\","
|
||||
+ "\"X-Content-Type-Options\":\"nosniff\"}"
|
||||
+ "\"X-Content-Type-Options\":\"nosniff\","
|
||||
+ "\"X-Frame-Options\":\"SAMEORIGIN\","
|
||||
+ "\"Referrer-Policy\":\"strict-origin-when-cross-origin\","
|
||||
+ "\"Permissions-Policy\":\"geolocation=(), microphone=(), camera=()\","
|
||||
+ "\"Content-Security-Policy\":\"default-src 'self'; script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com https://cdn.jsdelivr.net https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; frame-src https://challenges.cloudflare.com; connect-src 'self' https://api.stripe.com https://*.supabase.co; img-src 'self' data: https:; font-src 'self' data:\"}"
|
||||
}
|
||||
|
||||
fn handle_request(method: String, path: String, body: String) -> String {
|
||||
let inner_resp: String = handle_request_inner(method, path, body)
|
||||
fn handle_request(method: String, path: String, headers: Map, body: String) -> String {
|
||||
let inner_resp: String = handle_request_inner(method, path, headers, body)
|
||||
// Detect envelope already set by inner handler (starts with
|
||||
// {"el_http_response":1). If so, let it pass through unmodified —
|
||||
// the status code it carries takes precedence and we must not
|
||||
@@ -2121,6 +2227,7 @@ let stripe_pub_key: String = env("STRIPE_PUBLISHABLE_KEY")
|
||||
let stripe_price_founding: String = env("STRIPE_PRICE_FOUNDING")
|
||||
let stripe_price_professional: String = env("STRIPE_PRICE_PROFESSIONAL")
|
||||
let family_child_price: String = env("STRIPE_PRICE_FAMILY_CHILD")
|
||||
let stripe_webhook_secret: String = env("STRIPE_WEBHOOK_SECRET")
|
||||
let license_api_url: String = env("NEURON_LICENSE_API_URL")
|
||||
let resend_api_key: String = env("RESEND_API_KEY")
|
||||
let supabase_anon_key: String = env("SUPABASE_ANON_KEY")
|
||||
@@ -2145,7 +2252,13 @@ fs_write(html_path, page_html)
|
||||
|
||||
// Generate about page HTML.
|
||||
let about_html_path: String = src_dir + "/about.html"
|
||||
let about_html: String = page_open() + about_page() + page_close()
|
||||
let about_html: String = page_open_seo(
|
||||
"About Will Anderson — Neuron",
|
||||
"Neuron was built by one person. Will Anderson — engineer, founder, and the sole author of every line of Neuron's code. This is his story.",
|
||||
"/about",
|
||||
"Neuron was built by one person. Will Anderson spent nearly two years building the AI that remembers you — the memory architecture, the inference infrastructure, everything from the ground up.",
|
||||
"false"
|
||||
) + about_page() + page_close()
|
||||
fs_write(about_html_path, about_html)
|
||||
|
||||
// Generate terms pages HTML.
|
||||
@@ -2175,6 +2288,7 @@ state_set("__founding_sold_file__", sold_file)
|
||||
state_set("__founding_sold__", int_to_str(real_sold))
|
||||
state_set("__founding_total__", int_to_str(FOUNDING_TOTAL))
|
||||
state_set("__turnstile_secret_key__", turnstile_secret_key)
|
||||
state_set("__stripe_webhook_secret__", stripe_webhook_secret)
|
||||
persist_founding_count(real_sold)
|
||||
|
||||
println(color_bold("Neuron") + " - " + neuron_origin)
|
||||
@@ -2201,5 +2315,5 @@ println(" GET /api/supabase-config → public Supabase config (URL + a
|
||||
println("")
|
||||
|
||||
let port: Int = if str_eq(env("PORT"), "") { 3001 } else { str_to_int(env("PORT")) }
|
||||
http_set_handler("handle_request")
|
||||
http_serve(port, "handle_request")
|
||||
http_set_handler_v2("handle_request")
|
||||
http_serve_v2(port, "handle_request")
|
||||
|
||||
+56
-12
@@ -30,36 +30,80 @@ extern fn el_script_src(src: String, defer_load: Bool) -> String
|
||||
extern fn el_script_inline(js: String) -> String
|
||||
extern fn el_title(text: String) -> String
|
||||
|
||||
fn page_head() -> String {
|
||||
// ── Shared head infrastructure ────────────────────────────────────────────────
|
||||
// page_head_base() emits charset, viewport, favicons, fonts, CSS, scripts.
|
||||
// page_seo_block() emits the SEO/OG/canonical block for a given page.
|
||||
// page_head() assembles both for the homepage.
|
||||
// page_open_seo() is the variant used by inner pages with custom meta.
|
||||
|
||||
fn page_head_base() -> String {
|
||||
return el_meta_charset("UTF-8")
|
||||
+ el_meta("viewport", "width=device-width, initial-scale=1.0")
|
||||
+ el_title("Neuron - The AI That Remembers You")
|
||||
+ el_meta("description", "Every AI resets when you close the tab. Neuron doesn't. Runs on your machine. Remembers everything. Cheaper than ChatGPT on day one.")
|
||||
+ "<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/assets/favicon-16.png\">"
|
||||
+ "<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/assets/favicon-32.png\">"
|
||||
+ "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">"
|
||||
+ "<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>"
|
||||
+ el_link_stylesheet("https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;1,400;1,500&family=IBM+Plex+Sans:wght@300;400;500;600&display=swap")
|
||||
+ page_css()
|
||||
+ "<script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\" integrity=\"sha384-948ahk4ZmxYVYOc+rxN1H2gM1EJ2Duhp7uHtZ4WSLkV4Vtx5MUqnV+l7u9B+jFv+\" crossorigin=\"anonymous\"></script>"
|
||||
+ "<script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\" integrity=\"sha384-948ahk4ZmxYVYOc+rxN1H2gM1EJ2Duhp7uHtZ4WSLkV4Vtx5MUqnV+l7u9B+jFv+\" crossorigin=\"anonymous\" defer></script>"
|
||||
+ "<script src=\"https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2\" defer></script>"
|
||||
+ "<script src=\"https://challenges.cloudflare.com/turnstile/v0/api.js\" async defer></script>"
|
||||
+ "<noscript><style>.reveal { opacity: 1 !important; transform: none !important; }</style></noscript>"
|
||||
+ "<script async src=\"https://www.googletagmanager.com/gtag/js?id=G-Y1EE43X9RN\"></script>"
|
||||
+ page_ga_script()
|
||||
}
|
||||
|
||||
// page_seo_block — emits title, description, canonical, OG, and Twitter Card
|
||||
// for a given page. Pass the production canonical path (e.g. "/" or "/about").
|
||||
fn page_seo_block(title: String, description: String, canonical_path: String, og_description: String) -> String {
|
||||
let base: String = "https://neurontechnologies.ai"
|
||||
let canonical: String = base + canonical_path
|
||||
let og_image: String = base + "/assets/brand/neuron-wordmark-on-light@2x.png"
|
||||
return el_title(title)
|
||||
+ el_meta("description", description)
|
||||
+ "<meta property=\"og:type\" content=\"website\">"
|
||||
+ "<meta property=\"og:url\" content=\"https://neurontechnologies.ai\">"
|
||||
+ "<meta property=\"og:title\" content=\"Neuron - The AI That Remembers You\">"
|
||||
+ "<meta property=\"og:description\" content=\"Every other AI forgets you. Neuron doesn't. Runs on your machine, builds a memory over time, and gets sharper the longer you use it. Built by one person. April 22, 2026 \xe2\x80\x94 the meeting. April 25 \xe2\x80\x94 you're looking at the proof.\">"
|
||||
+ "<meta property=\"og:image\" content=\"https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png\">"
|
||||
+ "<meta property=\"og:url\" content=\"" + canonical + "\">"
|
||||
+ "<meta property=\"og:title\" content=\"" + title + "\">"
|
||||
+ "<meta property=\"og:description\" content=\"" + og_description + "\">"
|
||||
+ "<meta property=\"og:image\" content=\"" + og_image + "\">"
|
||||
+ "<meta property=\"og:site_name\" content=\"Neuron\">"
|
||||
+ "<meta name=\"twitter:card\" content=\"summary_large_image\">"
|
||||
+ "<meta name=\"twitter:title\" content=\"Neuron - The AI That Remembers You\">"
|
||||
+ "<meta name=\"twitter:description\" content=\"Every other AI forgets you. Neuron doesn't. Runs on your machine. Remembers everything. $19/mo or $199 founding member (first 1,000).\">"
|
||||
+ "<meta name=\"twitter:image\" content=\"https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png\">"
|
||||
+ "<link rel=\"canonical\" href=\"https://neurontechnologies.ai\">"
|
||||
+ "<meta name=\"twitter:title\" content=\"" + title + "\">"
|
||||
+ "<meta name=\"twitter:description\" content=\"" + og_description + "\">"
|
||||
+ "<meta name=\"twitter:image\" content=\"" + og_image + "\">"
|
||||
+ "<link rel=\"canonical\" href=\"" + canonical + "\">"
|
||||
}
|
||||
|
||||
fn page_head() -> String {
|
||||
return page_head_base()
|
||||
+ page_seo_block(
|
||||
"Neuron — The AI That Remembers You",
|
||||
"Every AI resets when you close the tab. Neuron doesn't. Runs on your machine. Remembers everything. Start free — no credit card required.",
|
||||
"/",
|
||||
"Every other AI forgets you. Neuron doesn't. Runs on your machine, builds a persistent memory over time, and gets sharper the longer you use it. Free tier available."
|
||||
)
|
||||
+ page_schema()
|
||||
}
|
||||
|
||||
fn page_open() -> String {
|
||||
return "<!DOCTYPE html><html lang=\"en\"><head>" + page_head() + "</head><body>"
|
||||
}
|
||||
|
||||
// page_open_seo — page shell for inner pages with unique per-page SEO.
|
||||
// title: full <title> tag text
|
||||
// description: meta description (120–160 chars)
|
||||
// canonical_path: path component, e.g. "/about" or "/checkout"
|
||||
// og_description: OG/Twitter description (can differ from meta description)
|
||||
// noindex: pass "true" to add noindex for non-public pages (e.g. checkout)
|
||||
fn page_open_seo(title: String, description: String, canonical_path: String, og_description: String, noindex: String) -> String {
|
||||
let robots_tag: String = if str_eq(noindex, "true") {
|
||||
"<meta name=\"robots\" content=\"noindex, nofollow\">"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
return "<!DOCTYPE html><html lang=\"en\"><head>"
|
||||
+ page_head_base()
|
||||
+ page_seo_block(title, description, canonical_path, og_description)
|
||||
+ robots_tag
|
||||
+ "</head><body>"
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,7 +1,7 @@
|
||||
// components/terms.el - Consumer Terms of Service page.
|
||||
// Returns complete HTML using the shared page shell from styles.el.
|
||||
|
||||
from styles import { page_open, page_close }
|
||||
from styles import { page_open_seo, page_close }
|
||||
from nav import { nav }
|
||||
|
||||
extern fn el_div(attrs: String, children: String) -> String
|
||||
@@ -13,7 +13,13 @@ extern fn el_a(href: String, attrs: String, children: String) -> String
|
||||
extern fn el_strong(children: String) -> String
|
||||
|
||||
fn terms_page() -> String {
|
||||
page_open() + nav() + terms_body() + page_close()
|
||||
page_open_seo(
|
||||
"Terms of Service — Neuron",
|
||||
"Read the Neuron Terms of Service. Governs your use of Neuron software and services provided by Neuron, LLC.",
|
||||
"/legal/terms",
|
||||
"The Neuron Terms of Service — governing your use of Neuron software and services provided by Neuron, LLC.",
|
||||
"false"
|
||||
) + nav() + terms_body() + page_close()
|
||||
}
|
||||
|
||||
fn terms_section_head(num: String, title: String) -> String {
|
||||
|
||||
Reference in New Issue
Block a user