NOTICE: All information contained herein is, and remains
the property of TechnoCore.
The intellectual and technical concepts contained
herein are proprietary to TechnoCore and dissemination of this information or reproduction of this material
is strictly forbidden unless prior written permission is obtained
from TechnoCore.
Our services support both HTTP/1.1 and HTTP/2 over HTTPS.
Any REST APIs built on HTTP/1.1 will continue to work transparently as before,
with no changes to be made to applications.
HTTP/2 is designed to use fewer connections so servers and networks will enjoy
less load. This is especially important when the network is getting congested
because HTTP/1’s use of multiple connections for parallelism adds to the
problem.
CachedStaticFiles ClassReplaces the previous NoCacheStatics and bare StaticFiles mounts.
Adds deployment-aware Cache-Control headers to all static file
responses served from resource.web/.
| Asset type | DEV | UAT | PROD |
|---|---|---|---|
| JS / CSS | 60 s | 300 s | 86400 s (1 day) |
| Fonts / Sounds | 420 s | 2100 s | 604800 s (7 days) |
Fonts and sounds use a 7x multiplier over JS/CSS because they change
very rarely.
_static_max_age(asset_type) -> intReturns the deployment-aware cache duration in seconds for a given
asset type. Called internally by CachedStaticFiles and by custom
routes that serve file content.
/documents/{path} Custom RouteServes documents from local.documents/ and data.documents/ with
per-document cache control. Features:
CacheTtl from def_document — each document row can specifyCacheTtl column.NULL or 0 means no explicit caching (browser heuristic applies)._DOC_TTL_CACHE dict — in-memory cache of DocName -> CacheTtl304 Not Modified when the browser sends a matchingIf-None-Match header...UPDATE def_document SET CacheTtl = 86400
WHERE DocName = ‘company_logo’;
Report HTML responses are cached in Redis with a TTL determined by:
effective_ttl = max(module_yaml_cache_ttl, _Server_ttl, REPORT_TTL_DEFAULT)
REPORT_TTL_DEFAULT (from ObjConstants) — fallback TTL whendef_report specifies a value.cache.ttl — read via get_cache_ttl() on the_Server_ttl — set on the report class (e.g._Server_ttl = REPORT_TTL_DOCUMENTATION for documentation reports).The largest of the three values wins, ensuring that module authors
and operators can both raise — but not lower — the cache duration.
<title> and SEO Metadata_render_template_legacy fills the template's {title} placeholder
from def_report.Title, and the {meta} placeholder with a full SEO
<head> block assembled by _build_seo_head().
_build_seo_head(context, page_title, description, site_name, base_url)
emits, driven entirely by def_report + the active package config:
<meta name="description"> — from def_report.ReportNotes, fallingseo.description.<link rel="canonical"> — base_url + /report/<context> (the URLog:type, og:site_name, og:title,og:description, og:url, og:image, og:locale.twitter:card (summary_large_image),twitter:title, twitter:description, twitter:image,twitter:site.name / url / logo / sameAsConfig (per-package), e.g. config.yaml → <pkg>:
site.company → og:site_name + JSON-LD name.site.logo → JSON-LD logo (and the og:image fallback).seo.description → sitewide description fallback.seo.image → og:image / twitter:image (use a ≥600px share photo).seo.locale (e.g. en_ZA), seo.twitter (@handle),seo.social (list → JSON-LD sameAs).Nothing here is package-specific — any report wrapped in a package
chrome that contains {meta} gets share-ready metadata for free. The
JSON-LD carries no $tokens$, so it survives process_text intact.
{title} or {meta}, so warm/cachedReportCode = <context> for the active package (plus CORE andSYSTEM).Previously {title}/{meta} were simply stripped, so report pages
had an empty <title> and no share metadata.
Every template slot — {title}, {meta}, {script}, {style} and
the whole Server.CLEAR_SLOTS list — is also a perfectly ordinary
JavaScript identifier. ${title}, ${start} and ${end} are
template literals, not slots, and by the time these replacements run
the page's own scripts are already inside html_content.
So they all go through _replace_outside_code(), which masks
comment / <style> / <script> regions first (the same mask
_expand_body_report_embeds uses for {report:X} — one predicate,
_mask_code_regions, not two copies of the regex).
Why it matters more than a corrupted token. Rewriting ${title}
leaves a bare $ in the script. process_text then calls
patch_param over the whole page before it masks scripts, and
patch_param tokenises on $: the stray one flips the pairing of
every later $, so the remainder of the document is swallowed as a
single $parameter$ and replaced with nothing.
The page is then served truncated mid-tag, with a 200 and a matching
Content-Length — no exception, no log line, and a browser that
simply renders half a page. Found 2026-08-12, where one ${title} in
a dashboard's step-chip JS cost every build card below it.
_slot_present() applies the same mask to the {title}/{meta}
guard, so a page whose JS merely mentions ${title} does not pay a
def_report lookup per request for a slot its template never had.
{t:key|English}_expand_body_translations() resolves translation markers in the
assembled body against def_translation
(ObjTranslation documents the
model and the reasoning).
It is called twice, around _expand_body_report_embeds:
| Pass | Why |
|---|---|
| before embeds | a translated sentence may wrap {report:X}, and word order differs per language |
| after embeds | an HTML-type report reached through an embed carries its own markers |
The second pass costs one substring check when nothing is left.
The fallback after | is the English, so a marker is inert until
someone translates it — adding markers to a live page is a no-op, and
a missing row can never surface a bare key to a visitor. The fallback
accepts one level of nested {...} so it can contain an embed; deeper
nesting is not supported.
Uses the same _mask_code_regions predicate as everything else here —
a {t: inside a page's own <script> is a reference, not a marker.
Do not re-inline that regex; the section above is what it costs.
The replacement is a callable, never a string: a translation
containing \1 or \g would otherwise be read by re.sub as a group
reference.
The marker pattern and the fallback rule now live in
ObjTranslation.expand_markers; this method resolves the language,
passes its own masking in, and delegates. They moved because while the
rule lived here, only an assembled page could be translated — a
form label, a button caption and an email line all reach
process_text and none of them reach this method. ObjProcessText
calls the same expander, so the page and the form cannot disagree
about what a marker is.
_mask_code_regions / _unmask_code_regions moved with it, up to
Objects.Object, for the same reason: the page pipeline and the text
pipeline must hide the same regions. Every call site here is
unchanged (self._mask_code_regions(...)), because they are
inherited.
GET /sitemap.xmlA route (serve_sitemap) returns the active package's sitemap as
application/xml. The body is built from SQL — the menu registry
(def_report with MenuRank > 0) — by ObjSitemap.cached_xml(), which
caches it in a module-level dict for the lifetime of the server session
(rebuilt on restart). Failures degrade to an empty <urlset> (never a 500
to a crawler). Caddy must handle /sitemap.xml → :9400 so it isn't served
by file_server. See factory.web/ObjSitemap.md.
GET /healthThe external liveness + readiness probe (serve_health). Public (in
PUBLIC_PREFIXES, so it bypasses the ACL login gate). Returns JSON
{status, service, package, database, redis, uptime_seconds}:
SELECT 1 and has_redis() on a fresh ObjData (returned to theclose_db()), and reports the resolved active packageObjects.global_config.package) so a sister-site misconfiguration is503 when it's unreachable; Redis is a200 "degraded" when only Redis is down; all-clear is 200 "ok".Caddy must handle /health → :9400 (it's in resource.config/caddy_routes.yaml
proxy_exact). Point an uptime monitor / load balancer at it. Mirrors the
shape of ServeAI's /health; gives every WebServer-based service one probe.
forwarded_allow_ips()Returns the addresses whose X-Forwarded-* headers uvicorn may honour,
built from firewall.web_trusted_proxy_ips — the same config the request
layer's _is_trusted_proxy_ip() reads, so the proxy is declared once.
Pass it to uvicorn.run(proxy_headers=True, forwarded_allow_ips=...).
A proxy missing from this list fails quietly, not loudly. uvicorn's
own default is 127.0.0.1, so it silently DISCARDS the forwarded headers
of any proxy that is not on loopback. Two things then go wrong and
neither raises:
ObjFirewall.auto_ban see one host instead of many;request.url.scheme stays http, and the AxionSession cookie isSecure when the scheme is https — so on a TLS site theSecure.Measured on sphinx (2026-08-18): Caddy reaches the app across the wg0
tunnel from 10.100.0.1, and every row in sys_user_login_attempts
recorded that address rather than the real client.
CIDRs are deliberately excluded from the returned value. uvicorn
compares literal peer addresses, so a range could never match while
still reading as "the proxy is trusted"; web_trusted_proxy_cidrs stays
available to the request layer, which does expand it, and the helper
logs when it drops one.
cythonize -3 -a -i WebServer.py
Compiling /home/axion/projects/axion/factory.web/WebServer.py because it changed..[1/1] Cythonizing /home/axion/projects/axion/factory.web/WebServer.py
Updated : 2025-10-02