AXIS CONSOLIDATED DOCUMENTATION — LLM CONTEXT PACK Generated: 2026-08-16 Source: ../axis/docs Public: https://hoox.sh/axis/docs Repository: https://github.com/hoox-sh/pyne FILE: ../axis/docs/architecture/adrs.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Architecture Decision Records" description: "ADR- through ADR- for AXIS: registry, AXIS≠engine, Solid+Vite, dual engines, configSchema, URL load, storage, CF project id, proxies, DO fan-out, migration, CORS, results export, transport preference + telemetry, on-chain data plane." --- Architecture Decision Records Formal ADRs for AXIS (product + frontend/). Status values: Accepted unless noted. Dates are conceptual product epochs, not git archaeology. Index | ID | Title | | --- | --- | | ADR- | Pluggable unified registry | | ADR- | AXIS ≠ engine | | ADR- | Solid + Vite product UI | | ADR- | Dual engines (server + Pyodide) | | ADR- | Declarative configSchema | | ADR- | URL-loadable plugins | | ADR- | Storage as a plugin | | ADR- | Frozen CF project id pynescript-axis | | ADR- | Worker proxies first | | ADR- | Durable Object stream fan-out | | ADR- | State namespace migration | | ADR- | CORS as deploy concern | | ADR- | Results export in AXIS | | ADR- | Transport preference + connection telemetry | | ADR- | On-chain data plane | --- ADR-: Pluggable unified registry Context Early AXIS registered sources/streams/engines in separate ad hoc maps. Operators and authors needed one mental model and one install path. Decision Adopt a unified PluginRegistry (frontend/src/plugins/registry.ts) with kinds: source | stream | engine | storage | component(reserved) Every plugin shares PluginBase (id, name, kind, description, version, builtIn, configSchema, capabilities, lifecycle hooks). Registration is ordered; listeners observe register/unregister. Consequences Active selection lives in the Solid store, not the registry. Built-ins protected from casual unregister. Catalogs (sources/catalog.ts, …) become registration facades. Contract docs under Plugins. --- ADR-: AXIS ≠ engine Context Closed chart hosts couple rendering and language runtime. That blocks offline mode, alternate evaluators, and headless reuse of PYNE. Decision AXIS never embeds a closed interpreter. All evaluation goes through EnginePlugin.run. UI modules call getActiveEngine() / runAndApply, not Python bindings directly (except inside the pyodide engine module). Consequences Language fidelity owned by PYNE. AXIS docs do not duplicate grammar theory. New engines (tiny demo, remote Worker, future WASM) are additive plugins. --- ADR-: Solid + Vite product UI Context Legacy vanilla JS shell (main.js, style.css) mixed DOM wiring with business logic and aged TV-blue tokens. Decision Product path is SolidJS + Vite + Tailwind , entry src/index.tsx / app.tsx. Imperative chart library (lightweight-charts) is isolated in ChartHost / PaneManager so Solid reconciliation does not thrash canvas DOM. Consequences Legacy files retained for smoke/static tests only (LEGACY.md). Icons via lucide-solid. Deploy artifact is Vite dist/, not repo-root static tree. --- ADR-: Dual engines (server + Pyodide) Context Researchers need server fidelity and offline demos. A single transport cannot satisfy both without compromise. Decision Ship two built-in engines: | Id | Role | | --- | --- | | server | POST {endpoint}/run?mode= with { script, data: bars } | | pyodide | Browser Python + vendored pynescript / antlr wheels | Both implement the same EnginePlugin contract and return RunResult. Consequences Endpoint field only for server-like engines. Pyodide requires correct static asset deployment (ZIP integrity checks). Timeouts unified at runner layer. --- ADR-: Declarative configSchema Context Per-plugin settings UIs do not scale; custom React/Solid forms per plugin forks the manager. Decision Plugins declare optional configSchema: map of field name → { type, default, label, options, min, max, ... }. Settings and library panels resolve defaults + pluginsConfig overrides via shared resolve helpers. Consequences New plugins get settings UI without shell changes (within field types). Types: string | number | boolean | select initially. Invalid user values remain a plugin responsibility at runtime. --- ADR-: URL-loadable plugins Context Third-party sources/engines should not require rebuild of the PWA for experiments. Decision Support loadPluginFromUrl: fetch ES module, validate shape, register, persist install metadata for restore on boot. Example plugins published under public/plugins/. Consequences Browser origin trust model: URL plugins are code execution. Security tests constrain schemes and storage-via-URL footguns. Offline restore needs prior successful fetch/cache. --- ADR-: Storage as a plugin Context Script library began as localStorage-only; cloud and git were bolted differently. Decision Storage is a first-class plugin kind with list/read/write/remove (+ optional draft/sync/status). Built-ins: local (IDB), cloud (Worker /api/scripts), git (GitHub/GitLab Contents API). Active storage in activePlugins.storage. Consequences Manager Script Library is backend-agnostic. Git commits only on explicit save; drafts local. Cloud uses Bearer Pro keys and optional optimistic concurrency. --- ADR-: Frozen CF project id pynescript-axis Context Renaming Cloudflare Pages/Worker projects severs KV/D/R bindings, custom domains, and CI secrets. Product brand evolved to AXIS. Decision Keep infrastructure name pynescript-axis. Brand in UI/manifest/docs is AXIS. Health JSON may identify pynescript-axis-worker. Optional DNS aliases (axis.) point at the same project without rename. Consequences wrangler.toml name and Pages --project-name stay frozen. Docs call out the freeze explicitly (this ADR). npm package name may lag brand. --- ADR-: Worker proxies first Context In-Worker Pyodide is attractive but heavy; production needed a path immediately. Decision Worker /api/run proxies to an external Python backend first (EXTERNALBACKEND). In-Worker runtime remains scaffolded/gated. Keys, usage metering, scripts API, and stream DO can ship independently of full edge evaluation. Consequences AXIS server engine talks to Worker URL transparently. Operational dependency on Flask (or equivalent) until edge runtime matures. Clear layering: Worker is data plane + auth, not necessarily the interpreter. --- ADR-: Durable Object stream fan-out Context N browser clients each opening venue WebSockets waste connections and risk rate limits. Decision SessionDO Durable Object: one upstream WS per session key; fan-out to N client sockets via /api/stream. Hibernation-friendly design on CF. Consequences Stream plugins may target DO URLs (example plugin provided). Session/symbol/interval query params are part of the contract. Not a historical source—live only. --- ADR-: State namespace migration Context Hard-renaming storage namespaces would brick user layouts and libraries if keys changed hard. Decision Canonical key pynescript.axis.v. On read, fall back to pynescript.axis.v (and parallel library/draft keys), then write forward. Do not keep dual-write forever. Consequences Seamless upgrade for existing browsers. Persistence strips bars/lastRun/logs. Tests cover migration (tests/state.test.ts patterns). --- ADR-: CORS as deploy concern Context Browser AXIS on origin A calling Pro API on origin B fails without CORS. Hardcoding origins in AXIS is wrong. Decision CORS is a backend/deploy configuration (ALLOWEDORIGINS on Flask; Worker headers as deployed). AXIS documents required origins; ops sets explicit lists in production (avoid perpetual ` outside demos). Consequences Localhost regex for dev. Troubleshooting centers on preflight, not engine code. Static file server does not replace API CORS. --- ADR-: Results export in AXIS Context Researchers need artifacts (trades CSV, run JSON) without a separate analytics service. Decision Results panel owns export: download JSON of lastRun, CSV of closed trades, clipboard helpers. Strategy pairing and stats run client-side from engine events (results/strategy.ts, results/events.ts). Consequences Export fidelity bounded by engine event quality. No server-side report store required for MVP. Viewer metrics ≠ broker statements (documented for operators). --- ADR-: Transport preference + connection telemetry Context Operators need to see how data and calculation move (WS vs REST vs local vs brokered), whether the live socket is actually open, and engine run latency. Naively “prefer WebSocket everywhere” is wrong: venues expose history over REST and live klines over WSS. Server engines batch-run over HTTP; Pyodide is local WASM. Decision . Transport policy History → REST (or local mock/CSV). Live → WebSocket first, paired per source via defaultStreamForSource (binance-rest → binance-ws, …). Engine → prefer WS /ws/run on the Pro API when available (flask-sock); fall back to HTTP POST /run. Pyodide remains local. Brokered → CF Durable Object / needsProxy streams surface as transport class broker. . Honest stream state — connecting until onStatus({ state: 'open' }); reconnect uses reconnecting / degraded telemetry, not false green. . Venue streams use reconnect with exponential backoff (streams/reconnect-ws.ts). . Connection HUD — StatusBar second row (ConnectionHud) reads ephemeral store.telemetry (SRC/STR/ENG/STO chips, tick pulse, engine latency). Persist only telemetry.hud prefs + live.preferAfterLoad / live.rerunOn. . Live re-run modes — every-tick (default) or bar-close (venue Bar.closed or bar time advance). . Chart stability — full setDataToChart only on history load (chartDataGen); live uses appendBar; overlay/script drawings update in place to avoid hide/show flash. Consequences UI never claims WS for historical load. Auto-live after Load is opt-in (preferAfterLoad, default off). Plugin capabilities may declare transport; otherwise HUD classifies from id/capabilities. See Streams, UI shell, Overview. --- ADR-: On-chain data plane Context AXIS began as an OHLCV-first chart host: SourcePlugin / StreamPlugin deliver Bar[] (open, high, low, close, volume) for CEX and similar venues, then engines run Pine against that series. Operators also need non-price market structure: | Need | Why Bar alone fails | | --- | --- | | Protocol TVL (USD) | Single scalar liquidity total, not OHLC | | DEX pool candles | Same shape as bars, but different venue semantics, address/network keys, and page caps | | Spike / unlock-style events | Discrete time markers with severity, not candles | | Honest provenance | Provider, snapshot cadence, finality — not implied by “the chart symbol” | Forcing these into the primary Bar pipeline would corrupt scale semantics (TVL vs price), invent fake OHLC for scalar metrics, and couple wallet/signing UX to read-only analytics. Decision Ship a parallel on-chain data plane orthogonal to the OHLCV source/stream path: . Plugin kind dataset on the unified registry (DatasetPlugin in src/plugins/types.ts): fetchDataset(opts) → OnchainDataset optional searchInstruments payload kinds: ohlcv | scalarseries | multiseries | events | table required provenance + finality (pending | safe | finalized | unknown) . Parallel plane modules under src/onchain/ (types, cache, keys, adapters, catalog, manager, events, alerts bridge) — not a new Bar field and not Pine output. . Worker allowlist proxy (worker/src/onchain.ts, routes under /api/onchain/…): rewrite only known DefiLlama / GeckoTerminal paths validate network ids and pool addresses short-TTL isolate cache (X-Axis-Onchain-Cache) client default bases from store.endpoint (src/onchain/proxy.ts) . Chart UX: attach as host-side overlays (left scale for scalars; separate series keys onchain); main market OHLCV stays on the right scale. Not wallet, not Pine plots. Alternatives considered | Alternative | Rejected because | | --- | --- | | Force into Bar / active source | TVL is not OHLC; faking open=high=low=close lies to engines and the price scale. Multi-protocol attach becomes multi-symbol load thrash. | | Piggyback Pine request.security only | Needs engine + script for every host overlay; no attach UI, no provider search, no finality UI. | | Browser → provider direct | Public DefiLlama / GeckoTerminal hosts often lack CORS for arbitrary AXIS origins (ADR-). | | Open reverse proxy (any URL) | SSRF risk; Worker must allowlist path shape, not pass-through arbitrary upstream. | | Wallet-connected RPC as the plane | Different product (signing, accounts, private RPC). On-chain here means public analytics APIs, not MetaMask. | Consequences CORS / deploy — browsers should use {endpoint}/api/onchain/llama|gecko; local Worker on : or deployed edge. Direct baseUrl overrides are for Node tests / trusted environments only. Finality honesty — DefiLlama TVL is typically a daily snapshot, not block-final chain state; Gecko OHLCV is pool sampling, not CEX marks. UI shows provenance lines; synthetic flags fabricated OHLC. Not a wallet — no signing, no injected providers, no private keys for this plane. Not Pine — overlays are host datasets; they do not replace indicator() / strategy() plots from the engine. Engines unchanged — active OHLCV source still feeds EnginePlugin.run; on-chain series do not silently become script close. Cache — client IDB dataset cache (instrumentCacheKey) plus Worker short-TTL memory; operators may see stale TVL briefly after large DeFi moves. Security — Worker rejects path traversal, invalid slugs/addresses, non-GET, and non-allowlisted Gecko paths. Phases shipped | Phase | Capability | Primary surfaces | | --- | --- | --- | | — TVL | DefiLlama protocol TVL scalar history + search | defillama-tvl dataset, src/onchain/defillama.ts, llama proxy | | — DEX | GeckoTerminal pool OHLCV + pool search; optional geckoterminal-ohlcv source for DSM multi-page backfill | geckoterminal dataset, src/onchain/geckoterminal.ts, gecko proxy | | — Events | Derived day-over-day TVL spike/drop events (tvlspike / tvldrop) | src/onchain/events.ts → chart markers | | — Alerts | Bridge events into alerts engine (onchaintvlspike / onchain_event) | src/onchain/alerts-bridge.ts, src/alerts/engine.ts | Pro-only DefiLlama surfaces (raises, unlocks via pro-api.llama.fi) remain out of the free Worker allowlist. Code map | Path | Role | | --- | --- | | src/onchain/types.ts | OnchainDataset, instruments, finality | | src/onchain/catalog.ts | Built-in dataset registration | | src/onchain/manager.ts | Attach / hide / detach store wiring | | src/onchain/proxy.ts | Resolve Worker llama/gecko bases | | worker/src/onchain.ts | Allowlisted proxy + health | | src/plugins/types.ts | DatasetPlugin contract | Related docs Datasets — plugin contract On-Chain data (end user) — operator guide Worker — /api/onchain/…` on the edge route table ADR-, ADR-, ADR- --- ADR lifecycle New ADRs should: . State context, decision, consequences. . Cross-link code paths. . Avoid re-litigating ADR- without supersession note. See also Overview Topologies Plugins contracts Datasets --- FILE: ../axis/docs/architecture/evaluation.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Evaluation map" description: "How AXIS runs Pine: Flask Pro API, Worker proxy, in-browser Pyodide. AXIS does not import PyneTS and does not place HOOX orders." --- Evaluation map Abstract AXIS is a charting PWA, not a language rewrite. Every Pine evaluation goes through an engine plugin. Built-ins are server (HTTP/WS to PYNE) and pyodide (in-browser Python wheel). Optional HOOX pyne-worker is just another Backend URL. AXIS does not import @hoox-sh/pynets. AXIS does not call trade-worker. Drawing a strategy on the chart is research, not execution. Conceptual model Interface surface | Path | Implementation | Notes | | --- | --- | --- | | Engine server | src/engines/catalog.ts | Prefer WS ws(s)://{endpoint}/ws/run (preferWs default true), else POST {endpoint}/run?mode= with { script, data, mode, inputs?, profiler?, libraries? }. Modes interpret \| compile \| auto. Timeout scales with bar count (–s). Default endpoint http://localhost:. | | Engine pyodide | same file | Self-hosted Pyodide .. (/pyodide/v../), wheels /vendor/pynescript-..-py-none-any.whl + antlr, runtime /pyodide/pynescriptruntime.py. Numba is not in Wasm; compile / auto is object-mode. Cold load ~–s. | | AXIS Worker /api/run | worker/src/runtime.ts | PYODIDEINWORKER=enabled scaffold; else proxy EXTERNALBACKEND/run; else NOBACKEND. Caps: KiB script, k bars, req/min/IP, s upstream. | | HOOX pyne-worker | src/workers/catalog.ts | Not a built-in engine. Paste origin as Backend URL. Distinct from the AXIS Worker (pynescript-axis). | | PyneTS | — | TypeScript library. PYNE docs. Not referenced in AXIS source. | Payloads follow the PYNE evaluate contract. Defaults: Flask POST /run uses mode auto. See modes. Internals | Path | Role | | --- | --- | | src/engines/catalog.ts | server + pyodide | | src/workers/catalog.ts | Runtimes Hub entries | | worker/src/runtime.ts | Edge /api/run | | public/pyodide/ | Wasm runtime + wheel | | scripts/sync-pyne-wheel.sh | Refresh vendored wheel | Invariants & edge cases . AXIS ≠ engine (ADR-). The shell never embeds a closed interpreter. . AXIS ≠ execution. Strategy events on the Results panel are not HOOX orders. See AXIS and HOOX. . No none stream plugin in src/streams/catalog.ts. Pausing live data is stopLive(), not a plugin id. . Pyodide wheel version can lag PyPI (.. wheel vs hoox-pyne ..). Check vendor/. . CORS: Worker allows localhost, .hoox.sh, .pynescript.ai, .pynescript.online, .pynescript-axis.pages.dev only. Worked examples Local desk ``bash terminal — PYNE Pro API cd pynescript && make run : terminal — AXIS cd axis && bun run dev ` Manager → engine server, Backend URL http://localhost:. Offline Switch engine to pyodide. First run downloads/caches the self-hosted runtime. Do not expect Numba. Edge proxy Deploy AXIS Worker with EXTERNALBACKEND pointing at Flask or pyne-worker. Engine stays server; endpoint is the Worker origin. Still no trade-worker hop unless that backend forwards events. Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | NOBACKEND | Worker has no EXTERNAL_BACKEND and Pyodide-in-worker off | Set backend or use browser Pyodide | | Compile "slow / different" in Pyodide | No Numba | Use Flask for numeric compile | | Expecting pynets in the PWA | Not wired | Import @hoox-sh/pynets` in your own app | | Strategy PnL on chart, no CEX fill | AXIS does not execute | pyne-worker live trading | See also Engines Worker runtime PYNE Pro API PyneTS HOOX pyne-worker AXIS and HOOX --- FILE: ../axis/docs/architecture/index.mdx Copyright (C) - jango_blockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Architecture" description: "AXIS system architecture: AXIS vs engine, plugin registry, topologies, ADRs, and state namespaces." --- Architecture AXIS architecture is the discipline of separable axes—price history, live time, calculation, and library storage—coordinated by a Solid AXIS UI that never owns a closed interpreter. Abstract | Layer | Responsibility | | --- | --- | | AXIS | UI, store, plugin orchestration, chart apply | | Plugin contracts | source \| stream \| engine \| storage \| dataset (+ reserved component) | | Engines | PYNE evaluation (browser Pyodide or remote /run) | | On-chain plane | Parallel non-OHLCV datasets (TVL, DEX, events) via Worker allowlist proxy — ADR- | | Optional edge | Cloudflare Pages + Worker + DO/KV/D/R | | Optional desk API | Flask Pro API | Invariant: AXIS ≠ engine. Evaluation always crosses an engine plugin boundary. Track map | Page | Contents | | --- | --- | | Overview | End-to-end data/control flow | | ADRs | ADR- … ADR- | | Topologies | Dev, static, edge deploy shapes | | State namespaces | Keys, migration, hash, IDB | Conceptual model Formal namespaces ``text Contract namespace: pynescript.axis.plugins.v App state key: pynescript.axis.v App state prefix: pynescript.axis. CF project id: pynescript-axis (frozen) Health service id: pynescript-axis-worker ` Related tracks End User — operator workflows UI — UI subsystem design Plugins — contracts & catalogs Worker — edge data plane DevOps — build & CORS PYNE runtime — language evaluation See also Root README.md — package-level map LEGACY.md` — pre-Solid shell boundary AXIS CLI — operator install / deploy --- FILE: ../axis/docs/architecture/overview.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Architecture overview" description: "End-to-end AXIS architecture: Solid AXIS UI, unified registry, dual engines, chart apply pipeline, and optional edge plane." --- Architecture overview Abstract AXIS is a composition host. The product thesis: own the axes (history, live, calc, library), swap the implementations. Shipping UI is Solid + Vite + lightweight-charts + CodeMirror ; calculation is never inlined into UI components. Conceptual model Interface surface (module map) | Concern | Primary paths | | --- | --- | | Entry | src/index.tsx, app.tsx | | State | src/store/ | | Plugins | src/plugins/{registry,types,loader,bootstrap,active}.ts | | Catalogs | sources/catalog.ts, streams/, engines/catalog.ts, storage/ | | Chart | chart/ChartHost.tsx, pane-manager.ts, drawing-layer.ts, price-precision.ts | | Editor | editor/ | | Run apply | indicators/runner.ts | | Results | results/, ui/ResultsPanel.tsx | | Workers Manager | ui/WorkersManager.tsx, workers/ | | Worker | worker/ | | CLI | packages/cli/ | | Desktop | src-tauri/, src/desktop/ | Legacy parallel: state.js, registry.js, main.js — not product path. See repo LEGACY.md. Control plane vs data plane | Plane | What moves | Who owns it | | --- | --- | --- | | Control | activePlugins, endpoint, theme, layout | Solid store + localStorage | | Historical data | Bar arrays | Source plugins → store.bars | | Live data | Bar updates | Stream plugins → multiplex → bars/chart | | Calculation | script + bars → RunResult | Engine plugins | | Library | ScriptDocument | Storage plugins | | Telemetry | plane connectivity / latency / ticks | Ephemeral store.telemetry + Connection HUD | Transport preference (ADR-) | Path | Preferred transport | Notes | | --- | --- | --- | | Load (history) | REST / local | Venue kline HTTP, CSV, mock walk | | Live | WebSocket | Venue WSS with reconnect; mock-poll local; DO = brokered | | Engine server | HTTP | POST /run batch; latency in HUD | | Engine pyodide | Local | Offline-capable after asset cache | Live pairs with history via defaultStreamForSource. Optional live.preferAfterLoad auto-starts WS after Load (default off). Dual engines | Engine | Transport | Fidelity host | | --- | --- | --- | | server | HTTP JSON { script, data } → /run | Flask or Worker→Flask | | pyodide | In-worker-thread-ish browser Python | Self-hosted wheels + pynescriptruntime.py | Timeout policy in runner scales with bar length (clamped). Live re-runs use shorter silent timeouts. Chart apply pipeline runAndApply: . getActiveEngine().run(...) . setLastRun . Optionally open results . Sync overlays in place (syncOverlayLines) — no blank destroy frame . Markers + strategy report side effects . Atomic Pine script drawings replace . Equity pane when appropriate (skip hide thrash on silent live re-runs) History vs live data path: loadBars bumps chartDataGen → ChartHost full setDataToChart + fit. Live ticks only manager.appendBar (never full setData/fitContent). PaneManager is imperative DOM under a Solid-owned outer shell—Solid must not reconcile the pane root (ChartHost invariant). Plugin registry PluginRegistry holds ordered maps per kind; events registered / unregistered. Built-ins register once (ensureBuiltins / catalog ensureRegistered). Dynamic plugins via loadPluginFromUrl. Active selection is not inside the registry—it is store state. Contract namespace conceptually: pynescript.axis.plugins.v (fields: id, name, kind, configSchema, capabilities, kind-specific methods). Edge plane (optional) Cloudflare project pynescript-axis: Pages: static dist/ Worker: /api/run proxy, keys/usage KV, scripts D, stream DO fan-out AXIS still speaks engine/storage plugins—not raw CF APIs in UI components Invariants . AXIS ≠ engine . Bars/logs/lastRun not fully persisted . Storage and engine endpoints may coincide (Worker) but remain separate plugin roles . Older keys migrate forward, never the reverse on write Failure modes (architectural) | Mode | Manifestation | Mitigation | | --- | --- | --- | | God-object UI | Logic in components | Keep runner/registry pure modules | | Engine leakage | Fetch /run from random widgets | Only engine plugins perform calc I/O | | Registry/store split brain | Flat engine ≠ activePlugins.engine | setActivePlugin keeps alignment | | SPA asset fallback | Pyodide loads HTML | Assert ZIP magic on assets | See also ADRs Topologies State namespaces AXIS store --- FILE: ../axis/docs/architecture/state-namespaces.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "State namespaces" description: "AXIS persistence keys, key migration, pluginsConfig, editor docs, library IDB, and URL hash state." --- State namespaces AXIS persistence is multi-homed: layout/config in localStorage, script library in IndexedDB (or remotes), ephemeral run data in memory, optional URL hash for shareable slices. Abstract | Namespace | Medium | Purpose | | --- | --- | --- | | pynescript.axis.v | localStorage | App shell state | | pynescript.axis.editor.doc | localStorage | Editor document backup | | pynescript.axis.storage | IndexedDB | Local library | | pynescript.axis.library.v | localStorage fallback | Library if no IDB | | pynescript.axis.library.draft | localStorage | Draft buffer | | Plugin install list | localStorage | URL plugins restore | | store.bars / lastRun / logs | memory | Session only | Contract / product identifiers (not storage keys): ``text pynescript.axis.plugins.v conceptual plugin contract id pynescript-axis CF project id (infra) ` Conceptual model App state key pynescript.axis.v Solid store (frontend/src/store/index.ts): Persisted (representative): Market: symbol, interval, exchange, source, engine, endpoint activePlugins { source, stream, engine, storage } pluginsConfig Layout: theme, editor, watchlist, panel open/width/height panes, scripts (indicator list metadata) live.streamId (and related live flags carefully) drawings (user annotations) Flat mirrors: source / engine aligned via setActivePlugin Explicitly omitted on persist: bars lastRun logs Debounced write (~ms). Legacy migration Read order: . pynescript.axis.v . else pynescript.axis.v On older-key hit: copy forward into pynescript.axis.v. Legacy vanilla state.js uses the same STORAGEKEY constant for the pre-Solid shell. pluginsConfig Map of configuration objects. Preferred keys: `text ${kind}:${id} e.g. storage:cloud, storage:git, engine:server ` Bare ids may still be read for compatibility (cloud, git). Values feed configSchema resolution inside plugins. Sensitive fields: apiKey, git token — browser-local; treat profile as secret store. Editor document | Key | Role | | --- | --- | | pynescript.axis.editor.doc | Draft / shared doc | | Bridge messages | Popout synchronization | Popout mode uses editor-bridge + shared storage so detached windows share source without re-fetching library. Local library IDB | Constant | Value | | --- | --- | | DB name | pynescript.axis.storage | | Version | | | Stores | scripts, kv | Migration flags (pynescript.axis.library.migrated) prevent re-import loops from older library keys: pynescript.axis.library.v pynescript.axis.library.legacy Cloud / git (remote namespaces) Not browser keys—server-side partitions: | Backend | Partitioning | | --- | --- | | cloud | Hash of API key on Worker; D table or memory | | git | owner/repo@branch + basePath | Revisions: cloud may expose revision / If-Match; git uses blob SHAs via forge APIs. URL hash state (legacy helper) frontend/src/state-hash.js (legacy state path): | Feature | Detail | | --- | --- | | Keys | symbol, interval, engine, source, stream, timeRange | | Script | base in hash if short | | Cap | ~ chars | | API | applyHashState, pushHashState, watchHashState | Solid-first product may not wire this by default; treat as optional share mechanism. Prefer library export + recipe docs for durable shares. Invariants . Migration is read-repair to AXIS keys. . Never persist full OHLCV in axis.v. . setActivePlugin keeps flat fields coherent. . Logs panel open state forced closed on hydrate (noise control). . Drawing tool resets to cursor on hydrate; geometries restore. Failure modes | Issue | Effect | Mitigation | | --- | --- | --- | | QuotaExceeded | persist no-op | Export library; shrink drawings | | Poisoned JSON | load defaults | Security tests; clear key | | Split profiles | “missing” library | Same browser profile | | Stale activePlugins id | missing plugin | Fall back / reselect built-in | Internals | Path | Role | | --- | --- | | frontend/src/store/index.ts | Solid persistence | | frontend/src/store/types.ts | AppState | | frontend/src/state.js | Legacy state | | frontend/src/state-hash.js | Hash sync | | frontend/src/storage/local.ts | IDB library | | frontend/src/plugins/loader.ts` | Installed plugin list | See also ADR- AXIS store Script library --- FILE: ../axis/docs/architecture/topologies.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Topologies" description: "AXIS deploy and dev topologies: Vite+Flask, static PWA, Cloudflare Pages+Worker, and offline Pyodide lab." --- Topologies Concrete process and network shapes for AXIS. Choose a topology; then compose plugins inside it (recipes). Abstract | Topology | AXIS | Calc | Live | Library | | --- | --- | --- | --- | --- | | Dev desk | Vite : | Flask : | Binance WS direct | local | | Static demo | axispwaserver : | Flask or remote | venue / mock | local | | Edge | CF Pages | Worker → Flask | DO fan-out optional | cloud | | Offline lab | any AXIS | Pyodide | mock-poll | local | Topology A — Dev desk Bring-up ``bash make run cd frontend && bun install && bun run dev ` Notes: Vite may proxy /run; Settings endpoint can still point absolute at :. CORS must allow Vite origin. Topology B — Static PWA + Pro API `bash cd frontend && bun run build python axispwaserver.py make run separate process ` VPS demo pattern: systemd units for axis-pwa and pynescript-api; ALLOWEDORIGINS includes AXIS origin. Topology C — Cloudflare AXIS at the edge Deploy sketch `bash bun run axis:deploy or: cd worker && bun run deploy cd frontend && bun run build wrangler pages deploy dist --project-name=pynescript-axis ` Frozen id: never rename pynescript-axis lightly (ADR-). AXIS config: Engine server, endpoint = Worker origin Storage cloud, same origin + API key Stream: direct venue or DO-backed plugin Topology D — Offline lab No Flask, no venue. Requires vendored pyodide + wheels on origin once. Topology E — Git-centric research Any of A–C for calc/data; storage axis = git. Forges are orthogonal network peers: `text AXIS --storage:git--> api.github.com | gitlab AXIS --engine:server--> Flask/Worker AXIS --source--> venue or CSV ` Port map (defaults) | Service | Port | | --- | --- | | Vite dev | | | Flask Pro API | | | Static PWA server | | | Wrangler Worker | | Comparison | Concern | A Dev | B Static | C Edge | D Offline | | --- | --- | --- | --- | --- | | HMR | yes | no | no | n/a | | PWA SW realism | partial | full | full | full | | Multi-tenant keys | no | no | yes | no | | Ops complexity | low | medium | high | low | | Fidelity | high (Flask) | high | high via proxy | engine-dependent | Failure modes by topology | Topology | Typical break | | --- | --- | | A | Flask not running; CORS localhost vs ... mismatch | | B | Stale SW; missing dist pyodide assets | | C | Unbound KV/D; wrong project name; EXTERNALBACKEND down | | D | First visit offline; SPA fallback for wheels | Internals | Path | Role | | --- | --- | | vite.config.ts | Dev/build | | axispwa_server.py | Static host | | worker/wrangler.toml | CF name + bindings | | packages/cli/ | AXIS CLI (setup / deploy / health) | | worker/README.md` | Edge ops | See also ADR-, , , Installation Worker DevOps --- FILE: ../axis/docs/devops/build-and-serve.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Build and serve" description: "Vite production build, dist layout, axispwaserver.py SPA rules, and asset caching." --- Build and serve Abstract Production AXIS assets are a static site produced by Vite (bun run build → frontend/dist). Serve them with any static host, Cloudflare Pages, or the repo’s axispwaserver.py SPA-aware server. Critical requirement: real binary assets for Pyodide (.wasm, .whl, .zip) must not be rewritten to index.html. Conceptual model Interface surface package.json scripts ``json "dev": "vite", "build": "vite build", "preview": "vite preview", "test": "bun test tests/ worker/tests/", "test:coverage:gate": "bun run test:coverage && bun scripts/check-coverage.mjs ", "test:security": "bun test tests/security/", "test:ee:smoke": "playwright test --grep @smoke", "test:all": "bun run test:coverage:gate && bun run test:security" ` axispwaserver.py `bash cd frontend bun run build python axispwaserver.py HOST / PORT env; default ...: → dist/ ` Behavior (frontend/axispwaserver.py): | Rule | Detail | | --- | --- | | Root | Serves frontend/dist | | Cache | /assets/ → immutable long cache; else no-cache | | SPA fallback | Unknown paths → /index.html | | Never rewrite | /assets/, /plugins/, /vendor/, /pyodide/, or extensions including .js/.css/.whl/.wasm/.zip/.py/.json… | This SPA exception list exists because Pyodide + micropip die with opaque BadZipFile when HTML is returned for a wheel URL. The engine’s assertZipAsset detects that class of failure early. Expected public assets | Path (public → dist) | Purpose | | --- | --- | | /plugins/.js | Example dynamic plugins | | /vendor/.whl | pynescript + antlr wheels | | /pyodide/v../ | Self-hosted Pyodide | | /pyodide/pynescriptruntime.py | Browser run bridge | Internals | Path | Role | | --- | --- | | frontend/package.json | Build scripts | | frontend/axispwaserver.py | Threading HTTP SPA server | | frontend/src/engines/catalog.ts | Asset URL expectations | | frontend/LEGACY.md | Product path vs old shell | Makefile note make pages-deploy may historically deploy the wrong tree — prefer: `bash cd frontend && bun run build wrangler pages deploy dist --project-name=pynescript-axis ` Worked example — static desk demo `bash cd frontend bun install bun run build PORT= python axispwaserver.py another terminal make run Flask : ` Open http://...:, engine endpoint http://...:. Invariants & edge cases . Service workers / offline — product path uses Vite PWA assets; legacy root sw.js is not the shipping SW. . Relative pyodide index — resolves against location.origin. . Do not hand-edit dist — regenerate from build. Failure modes | Symptom | Cause | | --- | --- | | Blank routes on refresh | Server lacks SPA fallback | | Pyodide HTML wheel error | Fallback rewriting .whl or missing vendor | | Missing plugins | public/plugins` not copied (check Vite publicDir) | See also Local dev Cloudflare Engines --- FILE: ../axis/docs/devops/ci-and-testing.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "CI and testing" description: "GitHub Actions for AXIS: unit coverage gate, Playwright smoke, worker typecheck, nightly full ee." --- CI and testing Abstract AXIS quality gates live primarily under frontend/ (Bun tests) and frontend/worker/tests/, orchestrated by GitHub Actions (.github/workflows/ci.yml, axis-nightly.yml). Deep test conventions are documented in Testing reference and frontend/TESTING.md. Conceptual model Interface surface — local commands ``bash cd frontend bun run test tests/ + worker/tests/ bun run test:unit bun run test:coverage:gate lcov + scripts/check-coverage.mjs bun run test:security bun run test:ee:smoke bun run test:ee:critical bun run test:ee bun run test:all gate + security ` Worker: `bash cd frontend/worker npm run typecheck ` Repo Makefile: `bash make test-frontend make worker-typecheck ` CI workflows ci.yml (excerpted jobs) | Job (names vary) | What | | --- | --- | | Frontend tests / coverage | Bun install, coverage, artifact axis-coverage-lcov | | axis-ee | Playwright @smoke on PR | | Worker typecheck | frontend/worker tsc | | Related | Python suite remains for pynescript core | axis-nightly.yml | Job | What | | --- | --- | | Playwright full | all ee specs | | Security + coverage gate | test:coverage:gate + test:security | Schedule: UTC + workflowdispatch. Internals | Path | Role | | --- | --- | | frontend/TESTING.md | Authoritative testing guide | | frontend/scripts/check-coverage.mjs | Scoped line gate | | frontend/playwright.config.ts | webServer build+preview : | | frontend/ee/ | Specs with @smoke / @critical tags | | frontend/worker/tests/ | auth, keys, scripts, runtime | | .github/workflows/ci.yml | PR gates | | .github/workflows/axis-nightly.yml | Nightly | Coverage policy (summary) Gate: % lines on a scoped core (plugins, storage minus idb, store, results, sources, streams, data, selected chart helpers, worker auth/keys/runtime/scripts). Excluded from gate: heavy chart UI, pyodide boot, legacy JS, Solid .tsx surfaces (unit-tested elsewhere or deferred). EE design Builds production preview; baseURL http://...:. Mocks /run and Binance where possible so smoke does not need live Pro API. Selectors use data-testid (axis-btn-load, axis-manager, …). Invariants & edge cases . No real network in unit tests — mock fetch / WebSocket. . Import ./setup first when touching store/plugins. . CI Bun version pinned in workflows (e.g. .). . Playwright browsers installed with --with-deps` on CI. Failure modes | CI red | Likely | | --- | --- | | Coverage gate | New untested code in scoped paths | | Smoke timeout | Build slow; webServer s | | Worker tsc | Env typing / DO exports | | Flaky ee | Missing testid; race without mock | See also Testing reference Local dev --- FILE: ../axis/docs/devops/cli.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "AXIS CLI" description: "packages/cli — install, doctor, Worker setup (D/OAuth), secrets, deploy, health — CLI-first operator path." --- AXIS CLI Abstract The AXIS CLI (@hoox-sh/axis-cli under packages/cli) is the CLI-first operator entry for local install, Worker bootstrap, Cloudflare secrets, deploy, and health checks. It wraps Bun + Wrangler with AXIS-specific paths and defaults (Worker name pynescript-axis, D pynescript, OAuth device flow). Requires Bun ≥ .. Product version: AXIS .. (CLI package version is independent). Conceptual model Install ``bash from monorepo root bun install cd packages/cli && bun install && cd ../.. bun run axis --help Make pass-through make axis ARGS="--help" Optional global link cd packages/cli && bun link && axis --help ` Root package scripts & Make targets | Invoke | Runs | | --- | --- | | bun run axis -- | packages/cli/bin/axis.js | | bun run axis:install | axis install | | bun run axis:doctor | axis doctor | | bun run axis:setup | axis setup | | bun run axis:deploy | axis deploy (default worker) | | bun run axis:health | axis health | | make axis ARGS="doctor --remote" | same bin with args | | make axis-install · axis-doctor · axis-setup · axis-deploy · axis-health | fixed wrappers | When using bun run axis setup -- --flag, keep the -- so Bun forwards flags to the CLI. Command surface | Command | Role | | --- | --- | | axis install | bun install root + worker/ + CLI | | axis doctor [--remote] | Toolchain, wrangler.toml, CF auth, optional live /health | | axis setup | Bootstrap: install → ensure toml → local D | | axis setup worker | Copy wrangler.toml.example → wrangler.toml if missing | | axis setup d --local\|--remote [--create] | Apply worker/schemas/scripts.sql | | axis setup oauth --github-client-id … | Set public OAuth App id in [vars] (or --secret) | | axis secret put\|list\|delete | Wrangler secrets (ADMINTOKEN, EXTERNALBACKEND, …) | | axis deploy / deploy worker | Deploy Worker pynescript-axis | | axis deploy pages | Vite build + Pages project | | axis deploy all | Worker then Pages | | axis health [--oauth] [--url …] | Probe /health and optional GitHub device start | | axis whoami | Cloudflare account | | axis dev | Vite product UI | | axis dev worker | Local wrangler Worker | | axis dev desktop | Tauri desktop shell | Global flags: --json, --quiet, -y / --yes. Production checklist `bash axis install axis doctor axis setup --github-client-id Ovli… --remote-d axis secret put ADMINTOKEN axis secret put EXTERNALBACKEND Optional but recommended for gated /api/run: axis secret put (or wrangler vars) REQUIRERUNAUTH / bind APIKEYS axis deploy worker axis health --oauth ` | Item | Guidance | | --- | --- | | GITHUBOAUTHCLIENTID | Public OAuth App id; Device Flow enabled on GitHub; env wins over body clientId | | GITLABOAUTHCLIENTID | Same for GitLab when using git storage | | ADMINTOKEN / EXTERNALBACKEND | Prefer axis secret put (not committed vars) | | APIKEYS KV | Bind in prod; D without KV fails closed (APIKEYSREQUIRED) | | ALLOWOPENKEYS | "" in production | | REQUIRERUNAUTH | "" to force Bearer on /api/run even without KV | | Project name | Frozen: pynescript-axis | Env overrides | Variable | Role | | --- | --- | | AXISROOT | Force monorepo root | | AXISWORKERURL | Default health / deploy probe URL | | CLOUDFLAREAPITOKEN | Non-interactive Wrangler auth | | AXISCLISRC= | Load src/ instead of dist/ | Internals | Path | Role | | --- | --- | | packages/cli/bin/axis.js | Bin entry (Bun) | | packages/cli/src/commands/ | Commander handlers (install, doctor, setup, deploy, secrets, health, dev, whoami) | | packages/cli/src/services/wrangler-toml.ts | Minimal toml var helpers | | packages/cli/src/services/health.ts | /health + OAuth probes | | Root package.json axis / axis: scripts | Convenience wrappers | | Root Makefile axis / axis-` | Make wrappers | See also: Installation, Cloudflare deployment, Worker bindings, Worker auth, packages/cli/README.md. --- FILE: ../axis/docs/devops/cloudflare.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Cloudflare deployment" description: "Pages + Worker for AXIS: frozen project pynescript-axis, CLI deploy, bindings, security checklist." --- Cloudflare deployment Abstract Production AXIS on Cloudflare is a split deploy: | Piece | Role | | --- | --- | | Pages | Static PWA (dist/) | | Worker | JSON API + WebSocket (worker/) | Project id: pynescript-axis — frozen infrastructure name (dashboard, wrangler, CI). Do not rename without a full binding/domain migration. Evaluation truth: Worker /api/run proxies to EXTERNALBACKEND unless the gated Pyodide path is fully implemented and enabled. You still need a Python host (VPS Flask, container, etc.) for real Pine Script fidelity today. Prefer the AXIS CLI for setup and deploy (bun run axis:deploy). Conceptual model Deploy procedure . Provision bindings (once) See Worker bindings: KV APIKEYS, USAGE D pynescript + schema Optional R, Durable Objects . Configure secrets / vars Production checklist: | Setting | Production guidance | | --- | --- | | EXTERNALBACKEND | Public HTTPS URL of Flask/PYNE API (axis secret put) | | ALLOWEDORIGIN | Extra exact origins (comma-separated); product hosts + .pynescript-axis.pages.dev are built-in — CORS | | ADMINTOKEN | Secret, strong | | APIKEYS KV | Bind in prod — D without KV fails closed (APIKEYSREQUIRED) | | ALLOWOPENKEYS | "" or unset | | REQUIRERUNAUTH | "" optional force Bearer on /api/run | | GITHUBOAUTHCLIENTID | Env preferred; never rely on body clientId alone | | PYODIDEINWORKER | "disabled" until wheel pipeline ready | . Deploy Worker ``bash Preferred: AXIS CLI bun run axis:deploy or: bun run axis deploy worker or: make axis-deploy Schema first (remote) bun run axis setup -- d --remote Equivalent raw wrangler cd worker bun install bun run deploy ` . Build & deploy Pages `bash bun run axis deploy pages or bun run build bunx wrangler pages deploy dist --project-name=pynescript-axis Make: make pages-deploy ` . Point the PWA Engine endpoint: Worker URL (if path-mapped to /run) or Flask URL directly Cloud storage endpoint: Worker origin Ensure CORS allows Pages origin Internals | Path | Role | | --- | --- | | worker/wrangler.toml / .example | Worker name + bindings | | worker/README.md | Ops narrative | | worker/package.json | deploy scripts | | packages/cli/ | axis deploy / axis health | | Makefile axis-deploy / pages-deploy / worker-deploy | Convenience targets | Health check `bash bun run axis:health or curl -sS https:///health service: pynescript-axis-worker ` Invariants & edge cases . Pages ≠ Worker host — CORS required unless same-origin routing via custom domain routes. . Localhost EXTERNALBACKEND is useless from the edge. . DO migrations apply on deploy when uncommented. . Observability enabled in wrangler for log tails (wrangler tail). Failure modes | Issue | Fix | | --- | --- | | NOBACKEND | Set reachable EXTERNALBACKEND | | CORS blocked | ALLOWEDORIGIN mismatch | | SPA HTML for wheels | Pages must serve /vendor and /pyodide` as static files | | Stream NO_DO | Enable SessionDO bindings | See also Worker CORS VPS demo (backend co-host) --- FILE: ../axis/docs/devops/cors-and-origins.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "CORS and origins" description: "Worker pickOrigin: local-dev, product hosts, project-scoped Pages previews, ALLOWEDORIGIN list, Flask constraints." --- CORS and origins Abstract AXIS is a browser product that talks to cross-origin backends (Flask, Worker, venues). CORS misconfiguration is the local-deploy footgun after missing wheels. The Worker implements an explicit origin picker (pickOrigin in worker/src/index.ts); Flask and venue APIs have their own rules. Conceptual model Worker behavior Resolution order for Access-Control-Allow-Origin: . Local-dev (always echoed) Any http:// or https:// origin on localhost or ..., optional port (e.g. Vite :, axispwaserver :, Playwright ephemeral ports). Not allowed (and not needed): http://...:… — browsers do not use ... as a page origin; binding with HOST=... only means “listen on all interfaces.” Localhost/ allowlisting is safe for CORS: only pages actually served from those hosts present that Origin. A public site cannot forge Origin: http://localhost: in a real browser. . Product hosts (..+) Exact product / HOOX / legacy PYNE hosts under: .hoox.sh / hoox.sh .pynescript.ai / pynescript.ai .pynescript.online / pynescript.online Examples: https://axis.hoox.sh, https://hoox.sh, https://pynescript.ai. . Project-scoped Cloudflare Pages (not open .pages.dev) Only origins matching .pynescript-axis.pages.dev (and the apex pynescript-axis.pages.dev) are echoed. Arbitrary third-party Pages projects (evil.pages.dev, other apps) fall through to the allowlist fallback — they are not reflected. . ALLOWEDORIGIN allowlist Comma-separated exact origins in env.ALLOWEDORIGIN (e.g. https://a.example,https://b.example). If the request Origin is listed, echo it; otherwise return the first entry (default https://pynescript.ai). CORS headers applied: `` Access-Control-Allow-Origin: Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization, X-Admin-Token, If-Match Access-Control-Max-Age: Vary: Origin ` OPTIONS → preflight. Production implication For a custom domain already under product regex, no extra var is required. For one-off preview hosts outside the project, list them: ` ALLOWEDORIGIN = "https://my-preview.example.com,https://axis.example.com" ` Flask / server engine The server engine posts from the browser to the configured endpoint. Flask Pro API must: . Answer OPTIONS preflight if cross-origin. . Reflect or allow the PWA origin. . Accept Content-Type: application/json. Pyne always appends the same product Origin regex as Worker pickOrigin (including .pynescript-axis.pages.dev) even when systemd ALLOWEDORIGINS is a short list. GET /health (AXIS Settings probe) and POST /run are free CORS paths — they echo any Origin so a Pages preview can reach https://axis.hoox.sh without a per-preview allowlist entry. Same-origin reverse proxy (VPS demo) eliminates CORS when the PWA and Pro API share https://axis.hoox.sh. Cross-origin Pages → VPS still needs those headers (they are now default). Venue sources/streams Public Binance/OKX/etc. REST must send CORS headers usable by browsers. If blocked: . Use offline mock-walk / mock-poll. . Add a same-origin proxy on Worker/Flask (not a general open proxy — scope carefully). . Prefer DO relay only for WS fan-out (WS is not CORS in the XHR sense, but still network-reachable). Dynamic plugins Module import of plugin JS: needs CORS on the script origin (same-origin /plugins/ is safe). fetch inside plugin: subject to third-party CORS independently. Invariants & edge cases . Credentials — Worker CORS does not set Allow-Credentials: true; use Bearer headers, not cookies. . Multiple prod domains — product regex + comma-separated ALLOWED_ORIGIN cover multi-host; unknown sites never get open reflection. . Vite vs — both covered for Worker; Flask config must match whichever you use. . Health checks from curl omit Origin — still work; browser calls need correct ACAO. . No open .pages.dev — only the frozen project pynescript-axis` previews are product-scoped. Failure modes | Browser console | Meaning | | --- | --- | | blocked by CORS policy | Origin not allowlisted | | preflight | Server lacks OPTIONS | | No ACAO on error body | Some error paths forgot headers (Worker try/catch mostly covered) | See also Worker bindings Local dev Dynamic loader --- FILE: ../axis/docs/devops/desktop.mdx Copyright (C) - jangoblockchained This file is part of AXIS. SPDX-License-Identifier: AGPL-.-only --- title: "Desktop (Tauri)" description: "Run and package AXIS as a native desktop app with Tauri ." --- Desktop (Tauri) Abstract AXIS ships as a browser PWA and as an optional desktop shell built with Tauri . The Solid/Vite UI is unchanged; Tauri embeds it in a system webview (WebKitGTK on Linux, WKWebView on macOS, WebView on Windows). Commands From the repo root (after bun install): ``bash bun run desktop:dev Vite : + Tauri window (hot reload) bun run desktop:build Vite production build + native installers bun run desktop:info toolchain / webview diagnostics ` | Script | What it does | |--------|----------------| | desktop:dev | Runs tauri dev → beforeDevCommand starts Vite, opens a native window on http://...: | | desktop:build | Runs tauri build → bun run build then packages dist/ | | desktop:info | Prints Rust, system libraries, and webview status | Installers land under src-tauri/target/release/bundle/ (AppImage, deb, rpm, msi, dmg — depending on host OS and bundle.targets). CI (GitHub Actions) Every push to main (and PRs that touch desktop/frontend paths) runs .github/workflows/desktop.yml: | Trigger | Result | |---------|--------| | Push main | Matrix build: Linux, macOS arm, macOS x, Windows → workflow artifacts (-day retention) | | Tag v / desktop-v | Same matrix + GitHub Release assets attached to the tag | | workflowdispatch | Manual re-run | Artifacts are named axis-desktop-{linux\|macos-arm\|macos-x\|windows}-{sha}. Concurrency cancels superseded runs on the same ref so each push rebuilds cleanly. Prerequisites Always Bun (or another JS package manager that can run the scripts) Rust (rustc / cargo) — see src-tauri/Cargo.toml rust-version System webview libraries for your platform (Tauri prerequisites) Linux (Arch / CachyOS example) `bash sudo pacman -S --needed webkitgtk-. base-devel curl wget file \ openssl appmenu-gtk-module libappindicator-gtk librsvg patchelf ` Debian/Ubuntu equivalents use libwebkitgtk-.-dev and related -dev packages (see the Tauri docs for the current list). Engine backends Desktop AXIS is the same product as the PWA. For Pine evaluation you still need one of: | Engine | Notes | |--------|--------| | Local pyne Pro API | http://...: — Vite proxies /run in dev | | Cloudflare Worker | Configure worker URL in Manager | | Pyodide (in-webview) | Fully offline path; largest first load | Layout | Path | Role | |------|------| | src-tauri/ | Rust host, tauri.conf.json, icons, capabilities | | src-tauri/tauri.conf.json | App id sh.hoox.axis, window size, build hooks | | src/ | Unchanged Solid product UI | | dist/ | Vite output consumed by frontendDist | Behaviour notes Service worker is skipped in the Tauri shell (src/pwa/register-sw.ts). Offline install is a PWA concern; the desktop app is already installed as a native binary. Window defaults: ×, min ×, centered. CSP is left open (null) so external venues, Pyodide assets, and worker APIs work the same as in the browser. Tighten later if you ship a locked-down build. Icons are generated from public/assets/icon-.png via bunx tauri icon. Native menu & open from disk The Tauri host builds a native app menu: | Menu | Item | Action | |------|------|--------| | File | Open Script… (⌘/Ctrl+O) | System multi-file dialog → library + editor tabs | | File | Quit AXIS | Quit (platform predefined) | | Help | About AXIS | Info dialog (version / license / site) | Flow | Path | Role | |------|------| | src-tauri/src/lib.rs | Menu, openpinescripts command, dialog + disk read | | src/desktop/ | isTauriShell, menu listen, open + About wiring | | src/storage/import-pyne-open.ts | Shared status/logs/editor open after import | | src/storage/import-pyne-files.ts | importPyneSources (text) + importPyneFiles (browser File) | Accepted extensions match the PWA drop path: .pyne, .pine, .pinescript, .pinev, .pinev`. Per-file size cap on the host: MiB. Drag-and-drop of the same extensions still works in the desktop webview. Conceptual model --- FILE: ../axis/docs/devops/harden-perf-audit-2026-08-11.md AXIS Hardening + Performance Audit Date: -- Scope: Specialist findings across security, hardening, reliability, and performance (Worker, client, desktop). Method: De-duplicate, drop false positives, rank by (severity × user impact × inverse effort). Spot-checked critical paths in-repo. --- Executive summary AXIS has a production-critical Worker auth gap: worker/wrangler.toml commits ALLOWOPENKEYS = "" with D bound and APIKEYS KV commented out. In that configuration, requireApiKey accepts any non-empty Bearer, partitioning the script library by hash of attacker-chosen tokens with no minting/revocation. Even with open keys off, unbound KV still accepts any well-formed pn… shape key. Alongside that, browser-callable Worker surfaces are overly open: CORS echoes any https://.pages.dev Origin, POST /api/run is unauthenticated (and uncapped/untimeouted), the GitHub device-flow OAuth proxy can accept body clientId and return repo-scoped tokens with weak rate limits, and the client plugin loader can dynamically import arbitrary HTTPS URLs into the app origin. On the product path, the highest user-visible reliability/perf debt is OHLCV cache thrash (getCachedBars prefers IDB over warm memory; DSM re-validates/full-loads every page), multi-MB DefiLlama protocol payloads cached whole, engine WS dead-client thrash, Session DO shared-upstream teardown, and full-series normalize/JSON on every live re-run plus editor keystroke cost. Recommended posture: Ship a small security + bars-cache PR first (S effort, outsized risk/perf), then /api/run + OAuth hardening, then WS/DO and live-tip apply path. De-dupe notes: Two identical server.ts ETag arrayBuffer() findings merged (kept as medium). /api/run auth and size/timeout findings treated as one top fix. Related bars-cache IDB issues kept separate in backlog (eviction, write amp, list getAll). False positives: None of the provided items were rejected after review. Severity of open auth is not overstated for any deploy that uses committed wrangler.toml vars against real D. --- Critical / High Critical | Area | File | Issue | Fix | Effort | |------|------|-------|-----|--------| | security | worker/wrangler.toml (+ worker/src/auth.ts) | Open keys + D without APIKEYS KV | Prod: unset/ ALLOWOPENKEYS, bind KV, mint-only keys; refuse open+D deploys | S | High — Security | Area | File | Issue | Fix | Effort | |------|------|-------|-----|--------| | security | worker/src/index.ts | PRODUCTORIGINRE allows any .pages.dev | Project-scoped Pages hosts or ALLOWEDORIGIN only | S | | security | worker/src/runtime.ts | Unauthed /api/run; no rate/size; hung proxy | requireApiKey in prod; rate limits; body caps; AbortSignal.timeout | M | | security | worker/src/git-oauth.ts | Body clientId; repo scope token return; weak RL | Env-only client id; reduce scope; Origin + durable IP limits | M | | security | src/plugins/loader.ts | Arbitrary remote import() + restore + host.fetch | Default-deny remote in prod; allowlist + SRI; sandbox; confirm restore | L | High — Reliability | Area | File | Issue | Fix | Effort | |------|------|-------|-----|--------| | reliability | src/engines/engine-ws.ts | Dead clients recreated every call → no cool-down | Cool-down TTL; skip WS while dead | M | | reliability | worker/src/durable-objects/session.ts | One client error kills shared upstream; no reconnect/cleanup | Per-client drop; backoff reconnect; hibernation cleanup | M | | reliability | src/engines/catalog.ts (Pyodide) | runPython sync; ignores AbortSignal; can freeze UI | Wall-clock timeout; async eval if available; wire real signal | L | | reliability | public/sw.js | Unbounded axis-runtime-v cache | LRU/max entries·bytes; keep strategy.ts in sync | M | High — Performance | Area | File | Issue | Fix | Effort | |------|------|-------|-----|--------| | performance | src/data/bars-cache.ts | getCachedBars ignores warm memory; stale IDB vs memory SoT | Memory-first; hydrate on IDB hit; count helper | S | | performance | src/data/data-source-manager.ts | Per-page full validate + double get for .length | In-memory density; validate at phase ends; bar-count helper | M | | performance | src/onchain/defillama.ts (+ worker onchain cache) | Full multi-MB protocol JSON for body.tvl only | Strip to name/slug/tvl; avoid full-body cache | M | | performance | src/indicators/runner.ts + src/streams/multiplex.ts | Full normalize + full OHLCV JSON every live re-run | Tip/delta payloads; tip-only map when length stable | M–L | | performance | src/editor/PyneEditor.tsx | Full doc.toString, tab map, stats, color scan per key | Debounce/incremental; delay materialization | M | --- Medium / Low backlog Medium — Security / hardening worker/src/auth.ts: ?key= query bearer → header-only; short-lived WS tickets; scrub logs (S). worker/src/durable-objects/session.ts: Unauthed /api/stream relay; symbol/interval into Binance URL; shared default session (M). src/ui/watchlist.js (+ chart.js, symbol-autocomplete): innerHTML with symbols/labels → textContent/escape + color allowlist (S). src/ui/panels/FloatableShell.tsx: postMessage(..., '') and no event.origin check (S). src-tauri/tauri.conf.json: app.security.csp: null → strict CSP (M). Medium — Reliability src/data/bars-cache.ts: IDB series never evicted (memory is capped) (M). src/data/bars-cache.ts: Full-series IDB put every putCachedBars during multi-page backfill (write amp) (M). src/storage/local.ts: migrateOnce sets migrated=true before durable success (S). src/alerts/webhook.ts: fireWebhook fetch without timeout (S). worker/src/onchain.ts: memCache full clear at → LRU; sticky protocols list (S). Medium — Performance src/ui/DataViewPanel.tsx: Crosshair rebuild clones all on-chain points (S). src/ui/VolumeProfileOverlay.tsx: ms poll + full-history recompute (S). src/ui/ScriptLogsPanel.tsx: Unvirtualized log For (M). src/chart/ChartHost.tsx: Theme JSON.stringify(overrides); stacked paint effects; inactive slot steals global manager (M). src/results/plot-visuals.ts: Full shape marker rebuild each run (M). src/data/bars-cache.ts / src/onchain/cache.ts: list via getAll() of full payloads for metadata (M). src/onchain/manager.ts: attach/refresh always network; ignore dataset cache TTL (S). src/data/watchlist-tickers.ts: Coinbase REST ≤ parallel calls for symbols (S). server.ts: ETag via full arrayBuffer() every GET (S) — de-duped from dual medium/low reports. Low src/storage/idb.ts: openDb missing onblocked / open timeout (S). src/ui/SystemLogs.tsx: Renders up to MAXLOGS () without virtualization (S). worker/src/scripts.ts: listD no LIMIT/cursor (S). High deferred from top- cut (still important) Pyodide main-thread freeze / ignored AbortSignal (src/engines/catalog.ts) — L effort, UX-critical when local engine selected. SW unbounded runtime cache (public/sw.js) — quota risk over time. --- Recommended first PR (– concrete file-level changes) Theme: “Fail closed on Worker identity + stop bars-cache self-DoS” — shippable in one review, low regression surface. . worker/wrangler.toml Set ALLOWOPENKEYS = "" (or remove). Document / uncomment APIKEYS KV binding with real id for prod. Keep open keys only in local/dev overlays, never in the committed prod-facing [vars]. . worker/src/auth.ts When env.DB (or script routes) are active and APIKEYS is unbound, do not accept open or shape-only keys. Prefer: require KV for any durable partition; reject with clear APIKEYSREQUIRED. . worker/src/index.ts Replace open (?:[\w-]+\.)pages\.dev with project-scoped host(s) (e.g. .pynescript-axis.pages.dev) and/or rely on ALLOWEDORIGIN list. Update worker/tests/cors-origin.test.ts accordingly. . src/data/bars-cache.ts getCachedBars: if peekMemoryBars(key) is warm, return a slice immediately; on IDB hit, hydrate memory then return. Add getCachedBarCount() (or extend getCachedRange) so DSM gap progress does not need full clones. . worker/src/runtime.ts (minimal slice if room) Reject oversized script / data arrays; AbortSignal.timeout on proxyToExternal. Full requireApiKey gate can land here or immediately in PR if scope must stay tiny. . Optional same-PR XSS quick wins src/ui/watchlist.js (+ symbol-autocomplete / chart label paths): stop unescaped innerHTML for symbols/labels. Out of first PR: OAuth redesign, plugin sandbox, DO session rewrite, tip-only engine protocol, editor incremental architecture, Tauri CSP (needs careful connect-src inventory). --- Out of scope notes Not invented: No issues beyond the specialist JSON; ranking and merge only. Deploy secrets / CF dashboard IDs: Binding real APIKEYS / USAGE KV ids is an ops step; code and wrangler.toml can enforce fail-closed but cannot invent production namespace IDs. External services: Binance/Coinbase/DefiLlama availability and their own rate limits; we only fix AXIS relay/cache behavior. Pine language / pyne Pro API contract changes: Tip/delta run payloads may need pyne coordination if the engine API must change; client-side tip-only apply can proceed without that. TradingView / trademark APIs: Not implicated; do not introduce fictional host SDKs while fixing XSS/CSP. Full plugin isolation architecture (iframe/worker sandbox, capability tokens): tracked as L; first step is default-deny + allowlist, not a complete plugin platform. EE of production OAuth apps / GitHub App rotation: Policy and App settings outside this repo audit. Desktop packaging / store review beyond setting a non-null CSP in tauri.conf.json. --- Suggested follow-on PR sequence | PR | Focus | Why | |----|--------|-----| | | Auth fail-closed + CORS + memory-first cache | Critical risk + S-effort perf correctness | | | /api/run gate, rate limits, body caps, proxy timeout | Stops public compute abuse | | | Git OAuth env-only client, scope, durable RL | Token theft / abuse surface | | | DSM walkBackRange + DefiLlama strip + attach cache-first | Bandwidth/CPU on data plane | | | engine-ws cool-down + Session DO lifecycle | Live chart stability | | | Tip-only apply + multiplex delta; editor keystroke | Sustained UI perf | | | Plugins default-deny + Tauri CSP + postMessage origin | Client hardening | | | SW LRU, IDB eviction/meta list, webhook/migrate/idb open | Backlog reliability | --- Ranking rubric (applied) . Severity (critical > high > medium > low). . User impact (data/account compromise, public compute cost, chart freezes, multi-MB downloads, every-keystroke jank). . Effort (prefer S/M for topfixes when severity ties). . Dependency (auth/CORS before metering; memory-first cache before DSM rewrite pays off fully). --- FILE: ../axis/docs/devops/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "DevOps" description: "Build, serve, Cloudflare, VPS demo, CORS, and CI for the AXIS PWA and Worker." --- DevOps Abstract AXIS DevOps spans five runnable surfaces: . Vite + Solid PWA — product UI (bun run dev) . Static server (axispwaserver.py or Vite preview) — production-like assets . Cloudflare Worker (worker/) — edge API / WS / OAuth / on-chain proxy . Desktop (Tauri ) — native shell around the same UI (bun run desktop:dev) . AXIS CLI (packages/cli) — install, doctor, setup, secrets, deploy, health See Desktop (Tauri) and AXIS CLI for operator entry points. Python evaluation for desk workflows still often uses the Flask Pro API (make run on :). The Worker proxies there when EXTERNALBACKEND is set. Conceptual model Topology cheat sheet | Mode | Command sketch | Ports | | --- | --- | --- | | Dev AXIS | bun run dev / bun run axis dev | Vite (often :) | | Dev + Flask | + make -C ../pynescript run | : | | Static dist | bun run build + python axispwaserver.py | : | | Worker local | bun run axis dev worker / cd worker && bun run dev | : | | CF Worker prod | bun run axis:deploy | HTTPS workers.dev / custom | | Operator | bun run axis:doctor · axis setup · axis health | — | Page map | Page | Topic | | --- | --- | | Local dev | Day-to-day loop | | AXIS CLI | Install, doctor, setup, secrets, deploy, health | | Build and serve | Vite build, SPA server, assets | | Cloudflare | Pages + Worker deploy | | Desktop (Tauri) | Native shell | | VPS demo | Single-box static + Flask | | CORS and origins | Allowed origins matrix | | CI and testing | GitHub Actions, coverage, ee | Frozen names | Surface | Value | | --- | --- | | CF project | pynescript-axis | | Health service | pynescript-axis-worker | See also Worker Testing reference AXIS hub --- FILE: ../axis/docs/devops/local-dev.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Local development" description: "Day-to-day AXIS loop: Vite, Flask, Worker wrangler, endpoints, and plugin examples." --- Local development Abstract Local AXIS development is a multi-process topology. AXIS is a Solid app under Vite; evaluation usually hits Flask; optional cloud/storage/stream features hit the Worker. Conceptual model Interface surface — commands Bootstrap (CLI-first) ``bash bun run axis:install app + worker + CLI bun run axis:doctor ` Frontend (preferred product path) `bash bun run dev Vite + Solid → typically http://...: or: bun run axis dev ` Desktop (optional Tauri shell) `bash bun run desktop:dev Vite + native window (see devops/desktop) or: bun run axis dev desktop ` Flask Pro API (engine backend) From the pyne / pynescript checkout (sister repo): `bash make -C ../pynescript run Flask → : ` Set PWA endpoint to http://...: for the server engine. Workers Manager and Settings can probe this URL. Worker `bash bun run axis dev worker http://...: or: cd worker && bun run dev ` Point cloud storage / Worker endpoint at http://...:. For /api/run proxy, set Worker EXTERNALBACKEND=http://...: (works on local wrangler; not on Cloudflare edge). AXIS CLI Preferred operator entry for install / doctor / setup / deploy — see AXIS CLI: `bash bun run axis install bun run axis doctor bun run axis setup bun run axis dev worker ` Makefile helpers | Target | Intent | | --- | --- | | make run-frontend | Historical Bun static server (frontend/server.ts) on : — legacy shell path | | make worker-dev | Wrangler dev | | make worker-install | Install worker deps | | make test-frontend | Bun unit tests under frontend/tests/ | For the Solid product, prefer bun run dev / bun run build over the legacy server.ts unless you are debugging the old tree (legacy shell). Internals — useful paths | Path | Role | | --- | --- | | frontend/src/index.tsx | App entry | | frontend/vite.config. | Vite / Solid plugin | | frontend/src/engines/catalog.ts | Default engine endpoint | | frontend/worker/wrangler.toml | Local vars | | frontend/public/plugins/ | Example modules served at /plugins/ | Worked loop . Terminal A: make run (Flask). . Terminal B: cd frontend && bun run dev. . Open Vite URL; set engine Server-Side, endpoint http://...:. . Load mock-walk if venues are blocked. . Optional Terminal C: Worker with ALLOWOPENKEYS= for cloud library experiments. Load an example plugin With Vite or static server: ` http://...:/plugins/example-coingecko-source.js ` Manager → Plugins → Load from URL. Invariants & edge cases . CORS — Flask must allow the Vite origin; Worker pickOrigin allows : and :. . Pyodide local — needs vendor wheels under public/ (copied to dist on build). . Two endpoints — topbar endpoint for engine vs cloud storage plugin config can differ (Flask vs Worker). . Legacy Makefile message still mentions AXIS and : Bun server. Failure modes | Issue | Fix | | --- | --- | | Engine not ready | Flask down / wrong port | | CORS errors | Align origins; see CORS | | Plugin | File only under src/plugins/ not public/plugins/ | | Worker scripts | Set open keys or mint pn_` key | See also Build and serve Worker local Plugin examples --- FILE: ../axis/docs/devops/vps-demo.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "VPS demo topology" description: "Single-box demo: static AXIS dist + Flask Pro API (+ optional reverse proxy) without Cloudflare." --- VPS demo topology Abstract A VPS demo is the lowest-surprise production-like setup for Pine evaluation: one machine runs Flask (PYNE) and serves the static AXIS build. Optional nginx/caddy terminates TLS and reverse-proxies /run same-origin to avoid CORS. No Worker required. Cloud storage and DO streams will not work without the Worker. Conceptual model Interface surface Processes | Process | Command | Port | | --- | --- | --- | | Build (CI or once) | cd frontend && bun run build | — | | Static | PORT= python axispwaserver.py | | | API | make run / python -m backend.app | | Same-origin proxy sketch (Caddy) ``caddy axis.example.com { handle /run { reverseproxy ...: } handle { reverseproxy ...: } } ` Then PWA endpoint = https://axis.example.com and the server engine posts to /run on the same host. Production-like (axis.hoox.sh) Cloudflare → VPS nginx : (TLS) nginx proxies SPA to static server and Pro API paths (/run, /ws/, /health, /lsp/, …) to gunicorn : PWA Backend URL = https://axis.hoox.sh (same origin — no mixed content) Cloudflare Pages previews (.pynescript-axis.pages.dev) also use that Backend URL cross-origin — pyne must allow those Origins on /health and /run (always-on product regex; see CORS) Update deploy (from a machine that can SSH to the VPS): `bash . Ship latest main git push origin main . On VPS (paths may vary — often /root/axis + rsync dist → PWA WorkingDirectory) ssh axis 'cd /root/axis && git pull --ff-only' Build on a machine with enough RAM, then rsync dist (VPS bun may OOM): rsync -az --delete dist/ axis:/root/pynescript/frontend/dist/ ssh axis 'systemctl restart axis-pwa' Pro API if needed: ssh axis 'systemctl restart pynescript-api' ` Local operator CLI for the Worker edge (separate from VPS static): bun run axis:deploy — see AXIS CLI. Hardened network (UFW) + health checks Production VPS pattern (verified on axis.hoox.sh): | Surface | Bind / allow | Health URL | | --- | --- | --- | | UFW | deny incoming default; allow /tcp, /tcp only | — | | nginx | ...: TLS | https://axis.hoox.sh/health → proxy ...:/health | | Pro API (gunicorn) | ...: only | loopback; not public | | PWA static | ...: only (nginx fronts it) | SPA at / | | failban | sshd jail | — | Do not open UFW for , , or on a public VPS. Direct probes to http://:/health must time out if hardening is correct. Workers Manager browser probes: On product hosts (axis.hoox.sh, …), pyne Pro is probed via same-origin /health (nginx), not http://...: (that would hit the client PC and always look “down”). Catalog publicEndpoint (https://axis.hoox.sh) is used when the default is loopback and the page is not local dev. CF Worker health stays on https://pynescript-axis..workers.dev/health (edge; independent of VPS UFW). Minimal without TLS Static :, Flask : Prefer a hostname over raw IP when the page is HTTPS Open firewall; accept CORS from static origin (configure Flask accordingly) Internals | Concern | Notes | | --- | --- | | Pyodide offline | Ship full dist including vendor + pyodide | | Process supervisor | systemd units for flask + axispwaserver | | Updates | Rebuild dist artifact; restart static only | | Secrets | Flask admin / pro keys separate from Worker | Worked example — systemd sketch `ini /etc/systemd/system/axis-pwa.service [Service] WorkingDirectory=/opt/axis/frontend Environment=PORT= ExecStart=/usr/bin/python axispwaserver.py Restart=on-failure ` Pair with an existing backend unit for Flask. Invariants & edge cases . CPU/RAM — Pyodide is browser-side; VPS load is Flask evaluate concurrency. . Do not expose open admin tokens. . Disk — dist + wheels are tens of MB. . Hybrid: VPS Flask as EXTERNALBACKEND` for a CF Worker still works if VPS has a public URL. Failure modes | Issue | Fix | | --- | --- | | Mixed content | HTTPS page calling HTTP Flask blocked | | CORS | Prefer same-origin proxy | | on /run | Flask crashed; check journalctl | See also Build and serve Cloudflare CORS --- FILE: ../axis/docs/enduser/getting-started/compose-recipes.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Compose recipes" description: "Lawful source × stream × engine × storage combinations: offline, desk, multi-venue, CSV, edge, and git library." --- Compose recipes AXIS treats composition as the product. This page lists recipes—complete four-tuples with intent, constraints, and failure modes—not marketing tiers. In the app: topbar Wire (or ⌘K → Open Architecture) starts from these predefinitions, then lets you swap or switch off any slot. Apply writes source / live.streamId / engine / activePlugins (and opens the on-chain panel when a dataset is selected). Abstract Formal active set: ``text Active set = { source, stream, engine, storage } ` Built-in ids (catalog may grow via URL plugins): | Kind | Built-ins | | --- | --- | | Source | binance-rest, mock-walk, csv-upload, (+ examples e.g. CoinGecko) | | Stream | binance-ws, mock-poll, none, (+ CF DO example) | | Engine | server, pyodide | | Storage | local, cloud, git | Recipe matrix | Recipe | Source | Stream | Engine | Storage | Needs net | Needs backend | | --- | --- | --- | --- | --- | --- | --- | | Offline lab | mock-walk | mock-poll | pyodide | local | first boot only | no | | Desk research | binance-rest | binance-ws | server → Flask | local | yes | Flask | | Multi-venue desk | binance-rest + URL sources | binance-ws / none | server | local | yes | Flask | | CSV desk | csv-upload | none / mock-poll | server or pyodide | local | no | optional | | AXIS at the edge | venue REST | venue WS / DO | server → Worker | cloud | yes | Worker (+ optional Flask proxy) | | Repo library | any | any | any | git | yes (save/list) | git host API | \CSV needs local file; engine may still call network if server. --- Offline lab Intent: airplane mode, demos, CI-less teaching, pure AXIS UX. | Axis | Id | Why | | --- | --- | --- | | Source | mock-walk | Synthetic OHLCV in-process | | Stream | mock-poll | Synthetic bar advance | | Engine | pyodide | In-browser pynescript via Pyodide | | Storage | local | IndexedDB script library | Setup . Install static or dev AXIS with public/pyodide + public/vendor present. . Select the four-tuple in topbar / settings. . Load → Run while online once (warm Pyodide). . DevTools → Offline; re-run. Invariants: no POST /run; no exchange traffic; drawings + library remain local. Failure modes: missing wheels → HTML SPA fallback errors; cold start timeout on huge bar sets. --- Desk research Intent: real venue history + live klines + full PYNE server fidelity. | Axis | Id | Why | | --- | --- | --- | | Source | binance-rest | Public REST klines | | Stream | binance-ws | Public WS kline stream | | Engine | server | Flask Pro API POST {endpoint}/run | | Storage | local | Fast offline library | Setup `bash make run : cd frontend && bun run dev ` Settings → Backend URL http://localhost: → Test. Symbol like BTCUSDT, interval h / d. CORS: AXIS origin must be allowed by Flask ALLOWEDORIGINS. Failure modes: rate limits; symbol not on venue; CORS; engine timeout on long history (AXIS scales timeout with bar count). --- Multi-venue / multi-source Intent: compare feeds without forking the AXIS. | Axis | Pattern | | --- | --- | | Source | Built-in binance-rest or Manager → Install URL plugin (e.g. CoinGecko example) → Use | | Stream | Match venue when available; else none for historical-only | | Engine | Usually server for parity | | Storage | local or git for shared research | Workflow . Manager → Catalog → capability badges (offline / auth / network / proxy). . Use sets active source. . Load and Run as usual. Dynamic plugins rehydrate from localStorage install list on boot (restoreInstalledPlugins). Failure modes: plugin URL not CORS-readable; invalid export shape; built-ins cannot be unregistered without force flags. --- CSV desk Intent: proprietary or offline CSV/OHLCV files. | Axis | Id | | --- | --- | | Source | csv-upload | | Stream | none (typical) or mock-poll for artificial advance | | Engine | pyodide (airgap) or server | | Storage | local | Workflow . Source → CSV upload (file picker opens if no file yet). . AXIS parses via parseOhlcvFile into upload store. . Load injects bars; symbol field less critical for synthetic series. . Run strategies against that tape. Invariants: bars stay in memory/session upload store—not the long-lived app state blob (size). Failure modes: bad headers/time units; empty parse; re-select same file requires clearing input (AXIS resets file input after pick). --- AXIS at the edge Intent: browser AXIS + Cloudflare Worker data plane (keys, usage, optional D scripts, DO fan-out). | Axis | Id | Why | | --- | --- | --- | | Source | venue REST (built-in or proxy-aware) | History | | Stream | venue WS or DO-backed example stream | Single upstream → N clients | | Engine | server endpoint = Worker | Worker proxies /api/run → Flask or future in-worker runtime | | Storage | cloud | /api/scripts + Bearer Pro key | Setup . wrangler dev / deploy Worker project pynescript-axis (frozen name). . Endpoint → Worker origin. . Storage cloud → API key from admin /api/keys. . Script Library uses cloud list/read/write; If-Match on concurrent writes. Invariants: CF project id is infrastructure, not brand; health JSON may say pynescript-axis-worker. Failure modes: missing Bearer key; D unbound → memory store (dev only); DO session query params wrong. --- Git library Intent: Pine sources as first-class repo files; Save = commit. | Axis | Id | | --- | --- | | Source / Stream / Engine | any research tuple | | Storage | git | Config (Manager → Script Library or pluginsConfig) | Field | Notes | | --- | --- | | provider | github \| gitlab | | token | contents:write / api | | owner, repo | GitHub path; GitLab projectId optional | | branch | default main | | basePath | default pine-library | | apiBaseUrl | self-hosted forges | Invariants: drafts stay local; only explicit Save/remove hit the remote. Commit message template supports {{name}} / {{iso}}. Failure modes: token scope; wrong base path; GitLab path vs numeric id confusion. --- Choosing a recipe Internals | Concern | Path | | --- | --- | | Active selection | frontend/src/store activePlugins | | Built-in catalogs | sources/catalog.ts, streams/, engines/catalog.ts, storage/catalog.ts | | URL plugins | plugins/loader.ts | | Compose UI | ui/Topbar.tsx, ui/PluginManager.tsx, ui/SettingsDialog.tsx` | See also Plugin contracts Manager and settings Topologies ADR- pluggable registry --- FILE: ../axis/docs/enduser/getting-started/installation.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Installation" description: "Install AXIS via the AXIS CLI, Vite+Solid dev, static dist PWA, desktop shell, or Cloudflare Worker backends." --- Installation AXIS ships as the axis package (Solid + Vite), version ... Primary path is the product UI under src/. Legacy static shell (main.js, root style.css) is not the product UI—prefer bun run dev or built dist/. Abstract Operator modes: | Mode | Command surface | When | | --- | --- | --- | | AXIS CLI (recommended ops) | bun run axis · make axis- | Install / doctor / setup / deploy / health | | Dev AXIS | bun run dev · axis dev | Day-to-day UI work | | Desktop shell | bun run desktop:dev | Tauri native window | | Static PWA | dist/ + axispwaserver.py : | Offline demo / VPS | | AXIS at the edge | axis deploy worker (+ optional Pages) | Production data plane | Backend is optional when Engine = Client-Side (Pyodide) and sources/streams are mock or CSV. Prerequisites Bun ≥ . for package install, Vite, and the AXIS CLI Python .+ if you use Flask Pro API (make run) or axispwaserver.py Modern Chromium / Firefox / Safari (PWA install works best on Chromium) Optional: Cloudflare auth (CLOUDFLAREAPITOKEN or wrangler login) for Worker deploy Optional desktop: Rust + platform webview libs — Desktop (Tauri) CLI-first bootstrap (recommended) ``bash From the AXIS monorepo root bun install cd packages/cli && bun install && cd ../.. bun run axis:install app + worker/ + CLI deps bun run axis:doctor toolchain, wrangler.toml, optional CF auth bun run axis setup ensure wrangler.toml + local D schema or: make axis-install && make axis-doctor && make axis-setup ` | Script / target | Equivalent | | --- | --- | | bun run axis | bun packages/cli/bin/axis.js | | bun run axis:install | axis install | | bun run axis:doctor | axis doctor | | bun run axis:setup | axis setup | | bun run axis:deploy | axis deploy worker | | bun run axis:health | axis health | | make axis ARGS="…" | pass-through to the CLI | | make axis-install · axis-doctor · axis-setup · axis-deploy · axis-health | Make wrappers | Full command surface: AXIS CLI. Dev AXIS `bash Terminal — Pro API (server engine) from sister pyne / pynescript repo make -C ../pynescript run Flask : Terminal — AXIS bun run dev Vite : or: bun run axis dev ` Open http://localhost:. Default symbol BTCUSDT, engine often server with endpoint http://localhost: (or demo host https://axis.hoox.sh). Desktop shell (optional) `bash bun run desktop:dev Tauri window + Vite HMR bun run desktop:build native installers ` See Desktop (Tauri). Edge Worker (local) `bash bun run axis dev worker wrangler : or: cd worker && bun run dev ` In AXIS: open Workers Manager (topbar activity icon) or Settings → set Backend URL to http://...:. Production project id is frozen as pynescript-axis — see topologies. Deploy checklist (prod) `bash bun run axis setup -- --github-client-id Ovli… --remote-d bun run axis -- secret put ADMINTOKEN bun run axis -- secret put EXTERNALBACKEND bun run axis:deploy bun run axis:health -- --oauth ` Production build (static) `bash bun run axis:install bun run build → dist/ python axispwaserver.py serves dist/ on : ` Confirm: App shell loads; chart requests history DevTools → Application → Manifest + Service Worker Icons / from public/assets/ Pyodide assets and vendor wheels under public/pyodide/ and public/vendor/ must be present in dist/ for offline engine—bun run build copies them via Vite public/. Offline-first lab (no Flask) . Source → Mock Walk . Stream → Mock Poll (or None) . Engine → Client-Side (Pyodide) . Storage → Local Disable network in DevTools; Run still executes. First Pyodide boot downloads/loads self-hosted runtime from the origin—allow that once while online, then go offline. CORS when using server engine Browser origin → Pro API must allow your AXIS origin. Flask uses ALLOWEDORIGINS. Localhost regex is typically included; for a VPS demo host, set an explicit origin list. Symptom of failure: POST /run blocked in Network tab (no Access-Control-Allow-Origin). Worker CORS: CORS and origins. PWA install Manifest: void theme ab, name AXIS Service Worker: cache-first shell; network-first /api/; offline API returns structured failure so Pyodide path remains usable Chrome/Edge: install icon in the omnibox Verification checklist | Check | Expect | | --- | --- | | Load | Bars on chart for default symbol | | Topbar | Source / Stream / Engine pickers populated | | Run (server) | Flask or Worker responds; plots overlay | | Run (pyodide) | Offline OK after warm-up | | Manager | Catalog lists built-ins | | Workers Manager | Health cards for local/prod backends | | Theme | Dark/light toggle persists | | axis doctor | Toolchain + wrangler green | Failure modes | Symptom | Likely cause | Fix | | --- | --- | --- | | Empty chart | Source network / CORS / wrong symbol | Load again; try mock-walk | | Engine errors immediately | Endpoint down or wrong URL | Settings / Workers Manager → Probe | | Pyodide “BadZipFile” / HTML | SPA fallback instead of wheels | Ensure public/vendor and pyodide in dist/ | | SW stale UI | Aggressive cache | Unregister SW or hard reload | | APIKEYSREQUIRED on scripts | D without KV | Bind APIKEYS or local ALLOWOPENKEYS= — Auth | Internals (repo paths) | Path | Role | | --- | --- | | src/index.tsx, src/app.tsx | Solid entry | | vite.config.ts | Build | | axispwa_server.py | Static host | | public/manifest.webmanifest, SW | PWA | | worker/ | Cloudflare Worker data plane | | packages/cli/ | AXIS CLI (@hoox-sh/axis-cli) | | src-tauri/` | Desktop shell | See also Quick start Compose recipes AXIS CLI DevOps local dev Desktop (Tauri) Worker --- FILE: ../axis/docs/enduser/getting-started/quick-start.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Quick start" description: "First historical load, first Pine Run, live stream, and reading the Results drawer in AXIS." --- Quick start Fifteen-minute path from empty AXIS to plots, strategy stats, and optional live updates. Abstract . Ensure AXIS is running (Installation). . Load bars for a symbol. . Paste or write a minimal indicator / strategy. . Run → inspect chart overlays + Results. . Optionally enable Live. Conceptual model Interface surface . Load history Topbar fields that matter: | Control | Default-ish | Notes | | --- | --- | --- | | Symbol | BTCUSDT | Venue-specific for binance-rest | | Interval | d | Watchlist + chart share interval vocabulary | | Source | binance-rest | Or mock / CSV | | Stream | binance-ws | Paused until Live | | Engine | server or pyodide | Settings for endpoint | Click Load (or pick a watchlist row). Status bar should move through loading → ready with bar count context. CSV path: Source = CSV upload → pick file when prompted → bars inject via upload store. Deep history: for multi-page backfill to a past date (background, with gap validation), open the Data panel — see Data Source Manager. . Minimal script Open the editor (docked right by default). Example indicator: ``pine //@version= indicator("AXIS smoke", overlay=true) plot(close, "close") plot(ta.sma(close, ), "sma") ` Strategy smoke (events for Results → Strategy): `pine //@version= strategy("AXIS strat smoke", overlay=true) longCond = ta.crossover(ta.sma(close, ), ta.sma(close, )) if longCond strategy.entry("L", strategy.long) if ta.crossunder(ta.sma(close, ), ta.sma(close, )) strategy.close("L") ` Language semantics: PYNE runtime. . Run Run (topbar or editor). Pipeline: . Active engine run({ script, bars, config }) . lastRun stored in memory (not fully persisted) . Results drawer opens . Chart applies overlay lines, trade markers, Pine drawings Success: status “Completed in Nms”; plots on price pane (or dedicated indicator pane if non-overlay). . Read Results Tabs: | Tab | Content | | --- | --- | | Events | Normalized entry/exit/order stream | | Strategy | Closed trades + win rate, PF, max DD | | Plots | Series names, point counts, last value | | Metrics | Runtime, engine, bar count, errors | | Raw | Full JSON payload | Export: JSON run dump; trades CSV. Click a trade to scroll the chart to entry/exit time. . Live (optional) Toggle Live. Stream plugin pushes bars; AXIS may re-run silently when needsRerun is set. Stream = None freezes time. Mock poll works offline after engine warm-up. . Save a script (optional) Manager → Script Library → name → Save. Backend is active storage (local default = IndexedDB). See Script library. Editor Pull / Push uses the same storage when configured for git/cloud. . Power tools (optional) | Tool | Where | Purpose | | --- | --- | --- | | ⌘/Ctrl+K | Global | Command palette — panels, Run, layout, editor toggles | | Workers Manager | Topbar activity · ⌘K | Backend health, presets, PYNE Agent install | | Layouts | Topbar | Multi-chart / H / V / + recipes | | Replay | Topbar | Scrub history (full bars, cursor on last) | | Compare | Topbar | Second symbol overlay | | Price decimals | Price-pane scale | Auto or – — Charting | | Debug / Pins | Editor header | Line chips vs chart markers — Debugging | | Layers / Alerts | Panel toggles | Drawings list; local price alerts | | v starter pack | Script Library → Import | examples/script-library-starter.json | Worked example: offline lab | Setting | Value | | --- | --- | | Source | mock-walk | | Stream | mock-poll | | Engine | pyodide | | Storage | local | Load → Run smoke indicator → toggle Live. No Flask required. Worked example: desk + Flask | Setting | Value | | --- | --- | | Source | binance-rest | | Stream | binance-ws | | Engine | server | | Endpoint | http://localhost: | | Storage | local | make run in repo root; Vite or static AXIS as installed. Invariants Empty editor Run is a no-op. Errors set status error and still populate Results Metrics/Raw when payload exists. Switching source auto-aligns default stream (e.g. mock-walk → mock-poll). Failure modes | Symptom | Action | | --- | --- | | CORS on /run | Fix ALLOWEDORIGINS / use Pyodide | | No trades in Strategy | Need entry+exit pair events; open strategy script | | Live no updates | Stream not none`; network; check logs drawer | | Pyodide first run slow | Expected warm-up; assets self-hosted under origin | See also Compose recipes Strategy and results Troubleshooting --- FILE: ../axis/docs/enduser/guides/alerts.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Alerts" description: "AXIS local alerts: price crosses, drawings, Pine conditions, on-chain spikes. Persist axis.alerts.v. Not HOOX orders." --- Alerts Abstract AXIS ships a client-side alert book (src/alerts/) with an Alerts panel. Kinds cover price, percent change, drawing touch, Pine conditions, and on-chain TVL spikes. State persists in axis.alerts.v. Optional browser notifications and a webhook URL are local to the PWA. These alerts are not HOOX trade-worker orders and are not the PYNE Pro API alert() L webhook export — though a Pine alert() / alertcondition() can surface as pinecondition if the engine returns it. Conceptual model Interface surface Kinds implemented in src/alerts/: | Kind | Fires when | | --- | --- | | pricecross | Price crosses a level | | priceabove / pricebelow | Price vs level | | pctchange | Bar-to-bar (or window) percent move | | drawingtouch | User drawing intersects price | | pinecondition | Engine-exported Pine condition / alert() | | onchaintvlspike | DefiLlama TVL jump (see On-Chain) | | onchainevent | Catalogued on-chain event | UI: src/ui/AlertsPanel.tsx. Persistence key: axis.alerts.v. PYNE language alert() / alertcondition() on Flask / pyne-worker is documented at PYNE alerts. Pointing AXIS's local webhook at the HOOX gateway is possible and dangerous — you are then inventing an execution path the PWA does not certify. Internals | Path | Role | | --- | --- | | src/alerts/ | Engine + format + storage | | src/ui/AlertsPanel.tsx | Panel | | src/onchain/ | TVL spike helpers (buildTvlSpikeEvents) | Invariants & edge cases . Alerts survive reload via local storage, not the Worker D script registry. . Closing the tab stops evaluation unless a stream + engine is still running. . On-chain kinds require the on-chain proxy (/api/onchain) to be reachable. . lastRun strategy results are not persisted; do not assume alert history equals a backtest. Worked examples . Open Alerts panel. . Add pricecross on the active symbol / TF. . Enable browser notifications in the browser, then in the panel. . For Pine: run a script that calls alertcondition; confirm pinecondition rows after a successful engine run. On-chain: attach a TVL series, then add onchaintvl_spike (default ~% day-over-day in buildTvlSpikeEvents). Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | No rows | Engine not running / no stream | Start live or replay bars | | Pine kind never fires | Engine dropped alerts | Use Flask interpret; see PYNE alerts | | On-chain silent | Proxy HTML / CORS | Use Worker /api/onchain, not the SPA origin | | Webhook posted a trade | You pointed it at HOOX | Use pyne-worker instead | See also On-Chain Drawings Evaluation map PYNE alerts AXIS and HOOX --- FILE: ../axis/docs/enduser/guides/data-source-manager.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Data Source Manager" description: "Background multi-page OHLCV backfill to a past date, dataset validation, and gap fills — without blocking the chart." --- Data Source Manager Deep history accumulation for a symbol + source + timeframe, down to a past date. Work runs fully in the background: chart, live streams, and the editor stay free. Abstract | Piece | Role | | --- | --- | | Topbar Load | One venue page (or historyBars) straight onto the chart | | Data Sources panel | Queue backfill jobs, watch progress, load cache onto chart | | Walk-back pages | Multi-page REST with endTime only (avoids start+end one-page traps) | | Validate + gap-fill | After backfill, check density and re-download holes | | IDB bars cache | Durable OHLCV per source\|symbol\|interval | Open from the topbar Data button, or command palette → Toggle Data Source Manager. Conceptual model How to use . Open Data (Data Sources panel). . Set symbol, source / exchange, timeframe, and accumulate to (UTC date). . Optionally check Apply to chart when complete. . Click Start background backfill — the job id appears immediately; work continues detached. . Watch phases: Backfilling → Validating → Filling gaps → Complete (or Partial). . Click Load to chart when ready (or rely on apply-when-complete). Pause / resume / cancel apply between pages. Multiple jobs queue with low concurrency (rate-limit friendly). Phases | Phase | Behavior | | --- | --- | | Backfill | Walk newest → older using endTime + page limit only. Never send startTime+endTime together to venue APIs that return the first N bars from start (would falsely complete in one page). | | Validate | Measure coverage from target past date → job end time: leading, internal, and trailing holes via interval step + tolerance. | | Gap-fill | For each gap window, walk-back download again; re-validate (several rounds). | | Done | datasetComplete when dense; otherwise Partial with remaining gap count (venue may lack older history). | Job fields (UI) | Field | Meaning | | --- | --- | | Bars / Pages | Cached bar count and network pages this job | | Oldest / Target | Current oldest open time vs past-date target | | Gaps | Detected holes · how many fill attempts landed bars | | Coverage | full / partial / in progress | Relation to Topbar Load | | Topbar Load | Data Source Manager | | --- | --- | --- | | Blocks UI | Yes (status loading) | No (background jobs) | | Depth | Single page / historyBars | Multi-page to past date | | Chart | Always paints | Only on Load to chart or apply-when-complete | | Cache | Ephemeral store.bars | Durable IDB bars-cache | Use Load for desk-speed symbol switches; use the manager when you need years of m/h/d history for research. Internals | Path | Role | | --- | --- | | src/data/data-source-manager.ts | Job queue, walk-back, validate, gap-fill | | src/data/bars-cache.ts | Memory-first + IDB durable OHLCV cache | | src/data/bars-gaps.ts | Gap detection / coverage report | | src/ui/DataSourceManagerPanel.tsx | Panel UI | | src/plugins/types.ts | SourceOpts: startTime?, endTime?, signal? | | src/sources/catalog.ts | Venue date params + sourcePageLimit | bars-cache performance (..+) bars-cache.ts is memory-first: | API | Behavior | | --- | --- | | getCachedBars | Serve warm in-memory series immediately; cold miss hydrates from IndexedDB then fills memory | | getCachedBarCount | Count without cloning the full series when warm | | Range / window | sliceBarsForLoad + count helpers avoid full clones for progress UI | | Soft caps | BARSCACHEMAX (k bars/series), BARSCACHEMAXSERIES ( keys) | DSM gap-fill progress uses getCachedBarCount instead of loading full series each tick. Limits & safety Max concurrent network jobs: (queue the rest). Caps: pages per walk, bars per series (soft IDB cap), gap-fill rounds. Backfill forces fallback: false so synthetic walks do not fake venue history. Cancel uses AbortController on in-flight fetches (symbol / job switches abort mid-page). See also Sources — built-in historical plugins Quick start — one-shot Load UI shell — topbar panels --- FILE: ../axis/docs/enduser/guides/drawings.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Drawings" description: "Interactive chart annotations in AXIS—tools, persistence, and how they differ from Pine script drawings." --- Drawings AXIS supports user drawings (interactive tools on the chart) and script drawings (objects returned by the engine). They share a canvas layer but different lifecycles. Abstract | Class | Origin | Cleared when | Persisted | | --- | --- | --- | --- | | User drawings | Drawing toolbar | Explicit delete / clear | Yes (store.drawings in pynescript.axis.v) | | Script drawings | Engine drawings[] | Next Run (re-applied) | No (recomputed) | Conceptual model Tools | Tool id | Kind | Geometry | | --- | --- | --- | | cursor | — | Select / pan mode (not a drawable) | | hline | hline | Horizontal price | | vline | vline | Vertical time | | trend | trend | Two points | | ray | ray | Two points (extends) | | extend | extend | Extended line | | rect | rect | Two corners | | ellipse | ellipse | Ellipse / oval | | arrow | arrow | Directional segment | | fib | fib | Two points; levels , ., ., ., ., ., | | measure | measure | Two points (delta readout) | | text | text | Anchor + label | Colors: default accent fff, up/down/measure variants in DRAWINGCOLORS. Layers & templates Layers panel lists user drawings (select, hide, delete) Templates: save/load drawing packs (axis.drawingTemplates.v) Duplicate / tag symbol helpers for multi-chart workflows Interface surface Toolbar on chart host (DrawingToolbar) Active tool in store; layer sync via createEffect on store.drawingTool Pane manager hosts price chart; drawings bind to time/price coordinates (not pixel-only) Workflows Annotate a level . Select Horizontal line. . Click price. . Reload page—line remains (persisted drawings array). Fibonacci between swing points . Select Fib. . Click swing low → swing high (or reverse). . Levels render from FIBLEVELS. After Run with Pine lines/labels . Run script that emits drawings. . Layer clears previous script drawings, then applies new set. . User drawings remain. Script drawings (engine → chart) Engine drawings[] map through pyne-drawings.ts before paint: | Engine surface | Chart behavior | | --- | --- | | line / box / label / polyline | SVG geometry on the script’s pane scale | | linefill / linefill | Filled quad between two line endpoints | | force_overlay (line/box/label) | Paint on the price pane even when the script is overlay=false | | barcolor series | Not SVG — per-bar candle body/wick tint via plot-visuals | Non-overlay scripts place ordinary geometry on the indicator pane Y-scale; user toolbar drawings always stay on price. Internals | Path | Role | | --- | --- | | frontend/src/chart/drawing-types.ts | Tool ids, types, fib levels | | frontend/src/chart/drawing-layer.ts | Interaction + render | | frontend/src/chart/DrawingToolbar.tsx | UI | | frontend/src/chart/pyne-drawings.ts | Map engine drawings → layer | | frontend/src/chart/ChartHost.tsx | Mount / dispose | Invariants . User drawings ≠ strategy results; deleting drawings does not alter lastRun. . Time base is bar time (unix-compatible chart times). . cursor never creates geometry. . Persistence omits bars/logs but includes drawings—large freehand-less sets only. Failure modes | Symptom | Fix | | --- | --- | | Clicks do nothing | Tool is cursor; chart empty (no bars/time scale) | | Drawings vanish on Run | Those were script drawings—re-run or convert notes to user tools | | Lost after wipe | Cleared site data / different browser profile | See also AXIS — charting Strategy and results --- FILE: ../axis/docs/enduser/guides/manager-and-settings.mdx Copyright (C) - jango_blockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Manager and settings" description: "Plugin Manager, Workers Manager (status + install helpers), and Settings for engine, storage, and endpoints." --- Manager and settings Two chrome surfaces configure AXIS: the unified Runtimes hub (Status + Plugins), and Settings. Models stay separate; entry chrome is shared. Abstract | Surface | Opens from | Mutates | | --- | --- | --- | | Runtimes → Status | Topbar Runtimes · ⌘K “Status” | Probe health; set endpoint / engine; install PYNE Agent plugin | | Runtimes → Plugins | Topbar Runtimes · ⌘K “Plugins” | Registry membership, active source via Use, script library | | Settings | Topbar gear | endpoint, active engine/storage, chart interval, watchlist refresh | Active plugins live in store.activePlugins; per-plugin fields in store.pluginsConfig keyed by ` ${kind}:${id} . Conceptual model Plugin Manager Catalog Lists sources, streams, engines, storages from the unified registry. | Action | Effect | | --- | --- | | Use | setActivePlugin(kind, id) for that row’s kind | | Capability badges | offline, needsAuth, needsNetwork, needsProxy from plugin.capabilities | | Built-in flag | Built-ins resist casual unregister | Status bar shows active engine id and storage backend after changes. Install (URL) Paste a module URL exporting a default plugin object (kind, id, name, contract methods). AXIS: . Fetches and validates export . Registers into the TypeScript registry . Persists install record so restoreInstalledPlugins() reloads next visit Security notes (operator-level): only https/http schemes suitable for module load; treat third-party URLs as code execution in your browser origin. See security tests under tests/security/. Supported URL kinds: source, stream, engine, dataset, component. Example plugins ship in public/plugins/: example-coingecko-source.js example-tiny-pyne-engine.js example-cf-do-stream.js PYNE Agent (sister project — natural language → PYNE scripts) installs as a component from your deployed worker URL (or via Workers Manager → Install): `text https:///plugin/axis-pine-agent.js ` See PYNE Agent and plugin reference. Works without the HOOX mesh. Script Library tab See Script library. Manager hosts the panel so library and plugins share one modal. Runtimes hub One dialog (RuntimesHub) with primary tabs Status | Plugins. Cross-links jump between them without closing. Implementation: src/ui/RuntimesHub.tsx hosts embedded WorkersManager + PluginManager (no nested dialogs). Status (Workers Manager body) Opens from topbar Runtimes (Status section) or ⌘K Open Runtimes → Status. | Sub-tab | Role | | --- | --- | | Overview | Health cards (distinct icon per worker) for pyne Pro API, AXIS Worker (prod + local wrangler), Pyodide, PWA service worker, optional PYNE Agent / pyne-worker | | Detail | Usage (when to pick this worker), probe latency, feature flags, Use-as-backend / preload / install actions | | Install | When to use + step-by-step setup with copyable commands (make run, wrangler, plugin URL, …) | | Configure | Paste Backend URL, presets (:, :, workers.dev, axis.hoox.sh), activate Pyodide, install agent plugin | Catalog items (usage) | Worker | Icon intent | When to use | | --- | --- | --- | | pyne Pro API | server | Primary Server calculation backend (Flask :). Best for compile/Numba and long history. | | AXIS Worker | zap | Production edge data plane (on-chain proxy, scripts D, optional /api/run). Default for On-Chain. | | AXIS Worker (local) | activity | Local wrangler : while developing the Worker. | | Pyodide | cpu | In-browser offline calc — no Backend URL; first load ~–s. | | Service Worker | wifi | PWA shell cache (auto in production; skipped in Vite dev). Not a calc engine. | | PYNE Agent | download | Optional NL → Pine plugin; scripts still run via your engine. | | pyne-worker | settings | Optional HOOX mesh edge evaluator; paste origin as Backend URL. | Probes Run once when the modal opens; Refresh re-runs. Each worker has a hard wall-clock timeout (~s) so a hung host cannot leave “Probing…” forever. Pyodide probe uses HEAD (not a full pyodide.js download). HTTP probes hit GET /health (JSON markers). Implementation: src/workers/, src/ui/WorkersManager.tsx. Operator CLI twin: AXIS CLI. Hardened VPS note: production UFW typically allows only + . Pro API binds ...: — public : probes correctly time out. On https://axis.hoox.sh, Workers Manager probes pyne Pro via same-origin https://axis.hoox.sh/health (nginx reverse-proxy), not loopback. See VPS demo. Settings dialog | Field | Behavior | | --- | --- | | Engine | Registry engine list; labels via engineOptionLabel | | Execution mode | When the active engine exposes configSchema.mode (server): interpret \| compile \| auto. Stored in pluginsConfig.engine:.mode and sent on each run (WS and REST). | | Prefer WebSocket run | Server engine: prefer /ws/run over REST when available | | Backend URL | Shown when engine is server or has configSchema.endpoint | | Test / Probe | GET health against endpoint; status message | | Storage | local \| cloud \| git | | Default interval | Updates store; may reload bars for current symbol | | Watchlist refresh | Clamp – seconds | | Chart theme | Preset picker (void / classic / mono / boutique dark & light) — see src/theme/presets.ts | | History bars | One-shot Load depth (venue may clamp further) | Deep multi-page history and gap repair live in the Data Source Manager, not Settings. Execution mode maps to PYNE Runtime.run(..., mode=…): | Mode | Behavior | | --- | --- | | interpret | Full AST interpreter (default) | | compile | Numba/numpy compiled path | | auto | Try compile; fall back to interpret on failure | The Connection HUD engine chip shows the selected mode. Save persists via Solid store persist() into pynescript.axis.v. Escape closes; Ctrl/Cmd+Enter saves. Pyodide engines hide endpoint—calculation is same-origin assets, not Flask. Interface surface (UI map) | Control | Store field | | --- | --- | | Engine select | activePlugins.engine, flat engine | | Storage select | activePlugins.storage | | Endpoint | endpoint | | Source/stream topbar | activePlugins., source, live.streamId | | Theme toggle | theme + data-theme on | Internals | Path | Role | | --- | --- | | src/ui/RuntimesHub.tsx | Unified Runtimes dialog (Status + Plugins) | | src/ui/PluginManager.tsx | Plugins body (catalog / install / library) | | src/ui/WorkersManager.tsx | Status body (probes / install / configure) | | src/ui/SettingsDialog.tsx | Settings | | src/ui/plugin-badges.tsx | Capability badges | | src/plugins/registry.ts | Unified registry | | src/plugins/loader.ts | URL load + restore (incl. component) | | src/plugins/bootstrap.ts | Built-ins | Invariants . Settings never write script bodies—only layout/config. . Catalog Use does not auto-Run; Load/Run remain explicit. . Source change may re-default stream (defaultStreamForSource). . API keys for cloud/git belong in pluginsConfig, not in git-committed defaults. Worked examples Point AXIS at local Worker . Settings → Engine Server-Side → Endpoint http://...: . Probe → OK . Save → Run Install example source . Manager → Install → URL to .../plugins/example-coingecko-source.js` (served origin) . Catalog → Source appears → Use → Load Failure modes | Symptom | Fix | | --- | --- | | Empty catalog | Built-ins not registered—hard reload; check console | | URL install fails | CORS on plugin host; bad export; mixed content | | Probe fails | Backend down; wrong path; HTTPS mixed content | | Settings not sticky | localStorage blocked / quota | See also Script library Compose recipes Plugins --- FILE: ../axis/docs/enduser/guides/on-chain.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "On-Chain data" description: "DefiLlama protocol TVL, GeckoTerminal DEX pool OHLCV, and derived TVL spike events — not a wallet, not CEX-only price." sidebarTitle: "On-Chain data" --- On-Chain data AXIS on-chain data plane: search public protocol and DEX metrics and plot them next to your chart. Built-in providers include DefiLlama protocol TVL (total value locked, USD), GeckoTerminal DEX pool OHLCV, and derived TVL spike/drop events. Abstract | Piece | Role | | --- | --- | | On-Chain panel | Search protocols / pools, attach / hide / detach series | | DefiLlama TVL | Daily USD liquidity history for a protocol slug | | GeckoTerminal OHLCV | DEX pool candlesticks (minute / hour / day aggregates) | | TVL spike events | Day-over-day \|%\| threshold events from attached TVL (tvlspike / tvldrop) | | Left price scale | Independent metric axis so main OHLCV keeps the right scale | | Provenance | Each series records provider + finality / snapshot notes | Open from the topbar Panels group (On-Chain), or command palette → toggle the On-Chain panel (panel id onchain). What this is not Not a wallet. AXIS does not connect to MetaMask, Ledger, or any signing wallet for this plane. Not CEX price by default. Protocol TVL is liquidity in USD, not the exchange candle series for the chart symbol. DEX pool OHLCV is on-chain pool price, which can diverge from centralized venues. Not Pine. These overlays are host-side dataset series; they are not produced by indicator() / strategy() plots. Conceptual model How to use (DefiLlama TVL) . Load a market as usual (symbol + source + timeframe). . Open On-Chain. . Search a protocol (e.g. aave, uniswap, lido). . Click a result to attach its TVL history. . The curve appears on the left scale of the price pane (when overlay wiring is active). . Hide or remove series from the Attached series list; Clear removes all. Multiple protocols can be attached (chart safety caps the number of on-chain lines). Refresh, export, popular presets Command palette (⌘K / Ctrl+K) exposes host jobs for attached TVL series — no Pine required: | Command id | Title | What it does | | --- | --- | --- | | onchain.refresh | On-Chain: Refresh Attached TVL | Re-fetch every attached DefiLlama TVL series (refreshAllAttachedTvl) | | onchain.export.series | On-Chain: Export Series CSV | Download all attached series as long CSV (series,time,value) | | onchain.attach.popular | On-Chain: Attach Popular TVL | Attach the top protocols by current TVL (attachPopularTvl()) | Related palette actions (panel / hygiene): | Command id | Role | | --- | --- | | panel.onchain | Toggle On-Chain panel | | onchain.mode.tvl | Open panel (TVL mode entry) | | onchain.clear.series | Detach all series | | onchain.clear.events | Clear event markers plane | | onchain.health | Probe Worker /api/onchain/health (Connection HUD) | Export format: one row per point — series,time,value — with time as unix seconds and value as USD TVL (or other scalar). Empty attachments still download a header-only file. Popular preset: empty DefiLlama protocol browse (highest TVL first), capped by the chart series limit (MAXONCHAINSERIES, currently ). Already-attached slugs refresh in place rather than duplicating. Refresh: re-runs the DefiLlama protocol TVL fetch for each attachment so left-scale lines stay current after long sessions. DEX pools (GeckoTerminal) Phase adds DEX pool OHLCV via GeckoTerminal public API (no API key). Use this when you want on-chain pool candles rather than protocol TVL: | Concept | Detail | | --- | --- | | Network | Gecko network id (eth, solana, base, arbitrum, polygonpos, …) | | Pool address | EVM x + hex, or Solana-style base | | Timeframe | minute / hour / day with aggregate (e.g. h → hour + aggregate ) | | Currency | Default USD when proxied with query pass-through | | Page size | Max candles per request (limit); Gecko hard cap | | Walk-back | endTime → Gecko beforetimestamp (bars strictly before that unix second) | Client helpers live in src/onchain/geckoterminal.ts (fetchGeckoPoolOhlcv, searchGeckoPools, interval/network maps). Browsers should use the Worker gecko proxy (below) because public GeckoTerminal hosts often lack CORS for arbitrary origins. Deep history (Data Sources + GeckoTerminal DEX) Topbar Load fetches a single page (up to bars). For multi-page history down to a past date: . Set the chart source to GeckoTerminal DEX (geckoterminal-ohlcv). . Use a pool symbol such as eth:x… (network + pool address). . Open Data (Data Source Manager) → set symbol / source / timeframe / accumulate to. . Start background backfill — AXIS walks older pages with endTime only (each page maps to beforetimestamp). The source does not multi-page inside one fetchHistorical call; DSM does it page-by-page. See Data Source Manager for job phases and cache behavior. Events (TVL spikes) From an attached TVL scalar series, AXIS can derive day-over-day percent-change events: | Field | Behavior | | --- | --- | | Type | tvlspike (gain) or tvldrop (loss) | | Default threshold | % absolute day-over-day change | | Severity | warn below critical band; critical for large moves (default ~% or .× threshold) | | Payload | pctChange, prevValue, value, thresholds | Pure helper: buildTvlSpikeEvents in src/onchain/events.ts. These are derived from daily snapshots — not mempool or block-final chain events. DefiLlama raises and token unlocks require Pro API (pro-api.llama.fi) and are not fetched by the free Worker proxy; inject external events only if you already have that data. Scale and chart behavior | Behavior | Detail | | --- | --- | | Scale | On-chain TVL lines use the built-in left price scale (left) | | Main market | Primary OHLCV stays on the right scale | | Compare | Compare overlays may share the left scale; on-chain keys are onchain, not overlay | | Units | TVL values are USD liquidity totals from DefiLlama; DEX OHLCV is pool quote (often USD) | Settings Settings → General → On-Chain is a short note only (no extra bindings): By default the PWA calls DefiLlama / GeckoTerminal directly (public CORS). Worker proxy ({endpoint}/api/onchain/llama / …/gecko) is used only when Backend URL is a real AXIS Worker (.workers.dev, pynescript-axis, or wrangler :). Not a wallet — public metrics only; no signing Keep Backend URL as the Pro API for Pine (https://axis.hoox.sh or local :). That host is not an on-chain proxy. Network path (CORS / Worker proxy) Public llama.fi / geckoterminal APIs currently allow browser origins (Access-Control-Allow-Origin: ), so AXIS defaults to direct fetch. Using https://axis.hoox.sh/api/onchain/… is wrong: nginx serves the SPA HTML, which produces invalid JSON / got HTML. The client now avoids that host for on-chain bases. When Backend URL is an AXIS Worker, paths map as: | Client request | Worker route | Upstream | | --- | --- | --- | | {endpoint}/api/onchain/llama/protocols | allowlisted GET | https://api.llama.fi/protocols | | {endpoint}/api/onchain/llama/protocol/{slug} | allowlisted GET | https://api.llama.fi/protocol/{slug} | | {endpoint}/api/onchain/gecko/networks/{net}/pools/{addr}/ohlcv/{tf} | allowlisted GET (+ query) | GeckoTerminal OHLCV | | {endpoint}/api/onchain/gecko/search/pools | allowlisted GET (+ query) | GeckoTerminal search | | {endpoint}/api/onchain/health | local | feature flags | Local Worker: http://...: + cd worker && bun run dev if you want the allowlisted proxy. Override with plugin config.baseUrl when needed. Isolate memory caches short-TTL responses (X-Axis-Onchain-Cache: HIT|MISS): | Route class | TTL (approx.) | | --- | --- | | Llama protocols list | min | | Llama protocol detail | min | | Gecko OHLCV | s | | Gecko search | s | Provenance and finality Each attached series carries a short provenance line, for example: Provider: DefiLlama · protocol TVL (USD) Provider: GeckoTerminal · pool OHLCV Finality: Daily snapshot · not CEX price (TVL) or pool candle finality notes (DEX) Treat DefiLlama points as daily aggregates from the public API — not block-final on-chain events and not exchange marks. DEX candles reflect the pool at GeckoTerminal’s sampling, not a CEX order book. Network, CORS, and proxies | Situation | What to do | | --- | --- | | invalid JSON / got HTML for aave | Backend URL is a SPA host (e.g. axis.hoox.sh) — leave it for Pine; on-chain should hit llama.fi directly (reload after upgrade) | | Local dev blocked by CORS | Point Backend URL at wrangler : Worker, or set plugin baseUrl | | Self-hosted demo | Optional: reverse-proxy allowlisted /api/onchain/ to the Worker | | Offline | Needs network; there is no offline synthetic TVL/DEX walk by default | See also CORS and origins for Worker origin allowlisting. Dataset plugins (advanced) On-chain providers implement the structural dataset plugin contract (fetchDataset). Built-ins register via the on-chain catalog; dynamic URL plugins may install additional datasets. End users normally only use the On-Chain panel — see Datasets for the contract sketch. Related docs Data Source Manager — OHLCV backfill (separate from on-chain scalars) Sources — historical bar sources Worker overview — /api/onchain/… route table CORS and origins — browser cross-origin constraints --- FILE: ../axis/docs/enduser/guides/pyne-agent.mdx Copyright (C) - jangoblockchained This file is part of AXIS. SPDX-License-Identifier: AGPL-.-only --- title: "PYNE Agent" description: "Write scripts from natural language (PYNE Agent) in AXIS using the optional pyne-agent-worker Cloudflare plugin." --- PYNE Agent What it does Describe an indicator or strategy in plain language. The PYNE Agent (Cloudflare Workers AI) returns a full script you can insert into the AXIS editor, then run with your normal engine (local PYNE Pro API, edge worker, or Pyodide). You do not need the full HOOX mesh. The agent worker is usable standalone. Pine Script and TradingView are trademarks of TradingView, Inc. Cloudflare is a registered trademark of Cloudflare, Inc. Prerequisites . A deployed pyne-agent-worker origin (or local wrangler dev). . AXIS open in the browser (dev or production PWA). Optional: API key if the worker has APIKEY set. Install the plugin . Open Manager → Plugins → Install. . Paste: ``text https://pyne-agent-worker.cryptolinx.workers.dev/plugin/axis-pine-agent.js ` . Set endpoint to the same origin (no trailing slash). . Set API key if required. . Prefer v / strategy or indicator when you know the target style. AXIS loads component plugins from URL (Manager → Install, or Workers Manager → install agent). You can also open the worker’s built-in chat UI: `text https:/// ` or use the floating PYNE Agent button the plugin may inject after install. Typical workflow . Ask for a script (example: “v RSI strategy with ATR trailing stop and clear plots”). . Review the fenced Pine block. . Insert into editor (plugin action) or copy/paste. . Select your engine and Run as usual. . Iterate with follow-up messages (“use length input”, “overlay false”, …). Tips Prefer specific version (v / v) and script kind in the request. The agent may use a private knowledge base (docs + open examples). Quality improves after the operator ingests docs — not required to start chatting. Validation against pyne-worker is optional on the agent side; AXIS evaluation is still your engine plugin. Troubleshooting | Issue | Fix | | --- | --- | | Load error | Check URL HTTPS, CORS, and that the module is reachable | | Unauthorized | Match plugin apiKey to worker secret | | Weak answers | Ingest KB on the worker; still usable without it | | Cannot insert | Use copy; ensure plugin api.insertScript` is provided by host | See also Plugin deep dive Manager and settings Plugin examples --- FILE: ../axis/docs/enduser/guides/script-library.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Script library" description: "Save, load, import/export Pine scripts via local IndexedDB, cloud Worker, or git (GitHub/GitLab)." --- Script library The script library is the operator-facing surface of storage plugins. The editor holds the working document; the library is durable, named documents with metadata. Abstract | Backend | Id | Medium | Auth | | --- | --- | --- | --- | | Local | local | IndexedDB (pynescript.axis.storage), LS fallback | none | | Cloud | cloud | Worker /api/scripts (D or memory) | Bearer Pro API key | | Git | git | GitHub/GitLab Contents API | PAT | Active backend: store.activePlugins.storage (Settings or library picker). Conceptual model Service layer (listScripts, readScript, writeScript, …) never talks to engines. Interface surface Manager → Script Library: | Control | Behavior | | --- | --- | | Backend select | setActivePlugin('storage', id) | | Refresh | list() | | Name / description | Metadata for next write | | Save | Write current editor doc | | Load | read → inject editor (loadLibraryDoc / setDoc) | | Delete | remove | | Export JSON | Full library dump | | Import JSON | Bulk write (importLibraryJson) | | Cloud endpoint + key | Saved under pluginsConfig['storage:cloud'] | | Git fields | provider, token, owner, repo, branch, basePath, … | Status line: backend · remote · branch · connected · count. Pine Script v starter pack Repo ships a portable library dump of v indicators, strategies, and helpers: | Path | Role | | --- | --- | | examples/script-library-starter.json | Importable library JSON (//@version= scripts: RSI, MACD, ATR, MTF, sessions, math lib, …) | Import: Manager → Script Library → Import JSON → pick the file (or any prior export). Uses importLibraryJson with new ids so it merges into the active backend (local / cloud / git). Prefer //@version= for new scripts; engines accept v/v when the runtime supports them. Local backend DB name pynescript.axis.storage v stores scripts + kv Older library keys migrate once Drafts: optional saveDraft / loadDraft without polluting remote backends Works fully offline Invariant: browser profile wipe deletes local library—export JSON for backup. Cloud backend Authorization: Bearer Scripts partitioned by key hash on Worker Concurrent updates may use If-Match / revision → HTTP on conflict Default endpoint falls back to store.endpoint or http://...: Configure key in library panel or Settings-related config; keys stay in this browser’s localStorage state blob. Git backend | Invariant | Detail | | --- | --- | | Save commits | Each write/remove is a commit (+ push via API) | | Drafts local | No remote commit for draft buffer | | basePath | Default pine-library/ | | Providers | github, gitlab (+ self-hosted apiBaseUrl) | Token scopes: GitHub contents:write; GitLab api or writerepository. Published libraries (import emulator) library("Name") scripts can be published as versioned folders so other scripts can import owner/Name/ as alias: `` {basePath}/published/index.json {basePath}/published/{namespace}/{Name}/{version}/lib.pyne {basePath}/published/{namespace}/{Name}/{version}/manifest.json ` Library panel → Publish library (or a successful Run of a library() — auto, skipped if unchanged). Namespace defaults to the git owner, else user. Local cache always updates so Pyodide / offline runs resolve imports. The engine receives {namespace, name, version, source} and pyne registers them before import (interpret path). Worked examples Solo offline library . Storage = local . Write indicator in editor . Name sma-ribbon → Save . Reload page → Library → Load Team cloud . Admin creates key on Worker . Storage = cloud; paste endpoint + key . Save shared strategies . Second browser: same key → list Research monorepo . Storage = git; GitHub owner/repo; basePath research/pine . Save → commit message from template . PR review on GitHub UI Internals | Path | Role | | --- | --- | | src/ui/ScriptLibraryPanel.tsx | UI | | src/storage/service.ts | Facade | | src/storage/local.ts | IDB | | src/storage/cloud.ts | Worker API | | src/storage/git.ts | Git plugin | | src/storage/git-github.ts, git-gitlab.ts | Forges | | examples/script-library-starter.json | Pine v starter pack | Contract: Plugin types StoragePlugin`. Failure modes | Symptom | Cause | Mitigation | | --- | --- | --- | | Empty list after switch | Wrong backend / auth | Check status line + key | | cloud | Bad/missing key | Regenerate; re-save config | | cloud | Revision conflict | Re-read then write | | Git | Wrong owner/repo/basePath | Fix config | | Quota local | Huge scripts in LS fallback | Prefer IDB; export prune | See also Manager and settings Compose recipes — git / edge State namespaces --- FILE: ../axis/docs/enduser/guides/strategy-and-results.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Strategy and results" description: "How AXIS turns engine events into trades, stats, chart markers, equity, and exportable Results tabs." --- Strategy and results After Run, the engine returns a structured payload. AXIS AXIS normalizes events, pairs trades, paints markers, and exposes a Results drawer. Strategy math here is a viewer—not a brokerage. Abstract Engine RunResult (simplified): ``text status: success | error plots[] | series{} events[] — entries, exits, orders, custom drawings[] — Pine line/label/box objects meta{} — ms, overlay, scriptname, plotmeta, … error? ` AXIS stores this as store.lastRun (in-memory; not fully rehydrated from localStorage). Conceptual model Results drawer tabs | Tab | Operator value | | --- | --- | | Events | Chronological normalized stream (kind, time, price, id, dir) | | Strategy | Closed trades table + summary metrics | | Plots | Series inventory (points, last value) | | Metrics | Runtime, status, engine, source, bars, high-level stats | | Raw | Pretty-printed JSON for support / diffs | Strategy metrics Computed by buildStrategyReport: | Stat | Definition (viewer) | | --- | --- | | totalPnl | Sum of closed trade PnL (price units) | | winRate | % wins among closed trades | | profitFactor | Gross profit / gross loss | | avgTrade / avgWin / avgLoss | Means over closed set | | maxDD | Peak-to-trough on cumulative trade PnL path | | wins / losses / trades | Counts | Pairing rules: entry-like kinds open by id; exit/close kinds match id or sole open trade. Direction flips short PnL. Missing prices drop the event from pairing (with normalization attempting OHLC fill from bars when available). Chart coupling Trade markers on price pane (eventsToMarkers) Scroll-to-time on trade row click Equity curve series when strategy events warrant (buildEquityCurve) Status bar snippet: closed trade count · net PnL when available Export | Export | Content | | --- | --- | | JSON | Full lastRun | | CSV | Closed trades (tradesToCsv) | | Clipboard | CSV of trades when supported | Filenames: axis-run-.json, axis-trades-.csv. Interface surface Drawer height persisted (resultsPanel.height); open state toggled after runs (openResults default true for interactive runs) Silent live re-runs can skip auto-open Equity / indicator panes created on demand when non-overlay or strategy paths need them Worked example . Load BTCUSDT daily history. . Run a crossover strategy (see Quick start). . Results → Strategy: inspect win rate. . Click a green trade → chart jumps to entry. . Export CSV for a notebook. Internals | Path | Role | | --- | --- | | frontend/src/ui/ResultsPanel.tsx | Drawer UI | | frontend/src/results/strategy.ts | Trade pairing + stats | | frontend/src/results/events.ts | Normalize events, markers, equity | | frontend/src/indicators/runner.ts | runAndApply orchestration | | frontend/src/chart/series-factory.ts | Plot colors / series | Language-level strategy semantics: PYNE runtime. Invariants . Results are a function of last successful/error payload + current bars—changing bars without re-run can desync prices used for fill. . Viewer PnL is not currency-normalized; treat as relative tape units. . meta.overlay === false routes plots to an indicator pane, not price. . Pine drawings (script) are distinct from user drawing tools (Drawings). Failure modes | Symptom | Explanation | | --- | --- | | Events but trades | Only entries, or unpaired ids | | Empty plots | Series all null / name filtered (_` prefix hidden) | | Markers missing | Manager not mounted or events lack times | | Huge Raw JSON | Many events—export file rather than copy | See also Quick start AXIS — results and strategy AXIS — indicators --- FILE: ../axis/docs/enduser/guides/troubleshooting.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Troubleshooting" description: "Diagnose empty charts, CORS, engine failures, Pyodide assets, streams, storage, and PWA cache issues in AXIS." --- Troubleshooting Systematic triage for AXIS. Prefer logs drawer + Network tab + status bar before reinstalling. Abstract Failure domains: . Data plane — source / stream . Calc plane — engine / endpoint / Pyodide assets . Shell plane — store, SW cache, plugins . Storage plane — library backends Decision tree Data plane | Symptom | Checks | Fix | | --- | --- | --- | | Infinite loading | Network klines; status message | Correct symbol; try mock-walk | | CORS on REST | Binance/public APIs usually OK; custom sources may fail | Proxy plugin or Worker | | CSV no bars | Parse error in status | Fix columns/time; re-upload | | Wrong interval bars | Store interval vs request | Settings interval + Load | Calc plane — server engine | Symptom | Checks | Fix | | --- | --- | --- | | Failed fetch /run | Endpoint, CORS preflight | ALLOWEDORIGINS; Settings probe | | HTTP xx/xx | Response JSON message | Fix script; backend logs | | Timeout | Bar count × complexity | Shorter history; raise server resources | | Plots missing | status: success but empty series | Script has no plots; check Raw | Probe: Settings → Test endpoint (health GET). Calc plane — Pyodide | Symptom | Checks | Fix | | --- | --- | --- | | HTML instead of wheel | Content-Type / ZIP magic | Deploy public/vendor, public/pyodide into dist/ | | Slow first run | Cold runtime | Wait; preloadPyodide on idle | | Offline fail first visit | Assets never cached | One online warm-up | | Interpreter error | Results Raw / logs | Language issue → PYNE | Live streams | Symptom | Checks | Fix | | --- | --- | --- | | Live toggle no effect | Stream = none? | Select binance-ws or mock-poll | | WS errors | Console; venue symbol format | Uppercase BTCUSDT style | | Reconnect loop | Network flap | Logs drawer; stop/start Live | | DO stream | Worker session query | Worker docs; example plugin | Storage / library | Symptom | Checks | Fix | | --- | --- | --- | | Save no-op | Active storage; errors in panel | Read status line | | Cloud | Bearer key | Settings/library key field | | Git denied | Token scopes | PAT permissions | | Lost local scripts | Cleared site data | Import JSON backup | Chart / editor (recent surfaces) | Symptom | Checks | Fix | | --- | --- | --- | | Indicator blank / missing on chart | Oscillator on price scale; wrong pane id | Prefer overlay=false; re-run; see Indicators | | Editor no lines / zero height | Dock height chain | Detach/reattach or toggle panel; editor should fill right strip | | Editor covers Indicators | Side-by-side dock | Both open on right → Indicators left of Editor; resize each panel | | Replay shows one candle only | Session start | Replay starts at last bar with full history; Play at end restarts from bar | | No Debug chips / Pins | Toggles + log shape | Enable Debug / Pins; logs need line and/or bar_index — Debugging | | Completions thin | Builtins corpus | Re-sync pyne-builtins.json from PYNE; optional remote LSP | Shell / PWA | Symptom | Checks | Fix | | --- | --- | --- | | Old UI after deploy | Service Worker | Unregister SW; hard reload | | State weird | localStorage['pynescript.axis.v'] | Export scripts; clear key; reload | | Plugin vanished | URL install list | Re-install URL | | Theme wrong | data-theme | Toggle theme once | | Lost multi-chart slots | chartLayout in store | Named layouts menu; re-apply recipe | Logging System logs drawer: boot, pyodide ready, live re-run errors (appendLog) Status bar: ready / loading / running / error + short message Results → Raw: last engine payload Security-related Poisoned localStorage: app should tolerate parse failures (see security tests) Plugin URL schemes restricted Never paste production PATs into shared screen recordings Internals for deeper digs | Area | Path | | --- | --- | | Load history | data/load-symbol.ts | | Run pipeline | indicators/runner.ts | | Streams | streams/multiplex.ts | | Engines | engines/catalog.ts | | Store | store/index.ts | See also Installation FAQ DevOps Worker --- FILE: ../axis/docs/enduser/index.mdx Copyright (C) - jango_blockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "End User" description: "Install AXIS, compose source/stream/engine/storage recipes, and run research workflows without a proprietary chart host." --- End User AXIS is an installable charting PWA: load history, stream the present, evaluate Pine Script through a pluggable engine, and keep a script library on disk, IndexedDB, cloud, or git. This track is for operators and researchers who use the AXIS—not for plugin authors or Worker deployers. Language fidelity (grammar, builtins, runtime semantics) lives in PYNE. AXIS never reimplements the language; it hosts evaluation via engine plugins. Abstract You compose four axes: | Axis | Question it answers | | --- | --- | | Source | Where do historical OHLCV bars come from? (CEX venues or DEX pools) | | Stream | How does the live bar advance? | | Engine | Who evaluates the script? | | Storage | Where do saved scripts live? | | On-chain | Protocol TVL, DEX OHLCV, events — parallel plane (not a wallet) | Any lawful combination is valid. Offline lab, desk research against Binance + Flask, AXIS at the edge through a Cloudflare Worker, and a git-backed library are all first-class recipes—see Compose recipes. Conceptual model The shell (topbar, chart, editor, results, manager) is AXIS. Calculation is always engine.run({ script, bars, config }). Persistence of scripts is always a storage plugin. Interface surface | Surface | Role | | --- | --- | | Topbar | Symbol, interval, source/stream/engine pickers, Load, Run, Live, On-Chain | | Watchlist | Quick symbol switch + quote poll | | Chart | lightweight-charts panes, drawings, trade markers, on-chain overlays, price-scale decimals | | Editor | CodeMirror Pine Script document (docked / popout) | | Results | Events, Strategy, Plots, Metrics, Raw + export | | On-Chain | DefiLlama TVL, GeckoTerminal DEX, spike events, export / refresh | | Manager | Plugin catalog, URL install (incl. component / PYNE Agent), Script Library | | Workers Manager | Backend health probes, presets, install helpers | | Settings | Endpoint, engine, storage, on-chain proxy notes | | Desktop | Optional Tauri shell (bun run desktop:dev) | Getting started . Installation — AXIS CLI, Vite dev, desktop, static dist/, or hosted PWA . Quick start — first load + first Run (//@version=) . Compose recipes — offline, desk, multi-venue, CSV, edge, git Guides Manager and settings — Plugins + Workers Manager PYNE Agent — natural language → scripts On-Chain data — TVL, DEX pools, events, Worker proxy Data Source Manager Script library — incl. v starter pack import Strategy and results Drawings Alerts — local price / Pine / on-chain; not HOOX orders Troubleshooting Reference Glossary FAQ Invariants (operator-facing) . AXIS ≠ engine — switching engine does not change the chart library; switching source does not change the language runtime. . Active set is four-tuple — source, stream, engine, storage are selected independently (with sensible defaults when source implies stream). . API keys stay in the browser — cloud storage and Pro keys are not uploaded by the shell except as request headers you configure. . Chart OHLCV is ephemeral — reload re-fetches for the topbar path; the Data Source Manager keeps a durable IDB bars-cache for deep history jobs. . Layout, script draft, and drawings survive in pynescript.axis.v. See also Architecture — ADRs and topologies Evaluation map — Flask / Pyodide / Worker / not PyneTS AXIS CLI — install / doctor / deploy UI — chart, editor, store internals AXIS and HOOX Plugins — contracts and catalogs PYNE runtime — evaluation semantics --- FILE: ../axis/docs/enduser/reference/faq.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "FAQ" description: "Frequently asked questions about AXIS, engines, offline use, TradingView relation, and data privacy." --- FAQ Product scope Is AXIS TradingView? No. AXIS is an independent charting PWA. Pine Script and TradingView are trademarks of TradingView, Inc. This project is not affiliated with or endorsed by TradingView, Inc. Trademark notice appears on the AXIS docs home. Does AXIS implement Pine Script? Evaluation is performed by engines that call PYNE (or proxies that do). The AXIS does not embed a closed interpreter. See PYNE for language fidelity. AXIS vs engine? AXIS = UI + orchestration. Engine = run(script, bars). You can swap engines without rewriting the chart host. That is ADR- territory—ADRs. Running & offline Can I use AXIS fully offline? Yes, with recipe Offline lab: mock-walk + mock-poll + pyodide + local, after one warm-up so Pyodide assets cache. Server engine and live venue streams require network. Why is first Pyodide run slow? Bootstraps Python + wheels in-browser. Subsequent runs reuse the runtime when still warm. Assets are self-hosted under the PWA origin when deployed correctly. Default endpoint points at a VPS—is that required? No. Change Settings → Backend URL to http://localhost: or your Worker. Defaults may reference a public demo API host for convenience. Data & privacy Where do API keys live? In this browser’s localStorage state / pluginsConfig. They are sent as HTTP headers only to endpoints you configure (cloud storage, etc.). They are not part of the Pine script body. Are OHLCV bars uploaded? Server engine sends script + bars to the configured endpoint for that run. Pyodide keeps bars local. Choose engine accordingly. Are bars saved on disk? App state persistence omits bars, lastRun, and logs to control size. History is re-fetched on Load. Features How do I share a setup? Legacy hash state (state-hash.js) can encode symbol/interval/engine/source/stream (and short scripts). Prefer documenting the four-tuple + library export for durable sharing. URL-load plugins are re-fetched by install list. Can I use my own data vendor? Yes—implement a source plugin (and optional stream) and install via URL or built-in registration. See Plugins. Why Strategy tab shows no trades? Need paired entry/exit-style events with prices. Indicators that only plot will fill Plots/Metrics, not Strategy stats. Drawings disappeared after Run Script drawings are cleared and re-applied each run. User toolbar drawings persist across runs. Popout editor out of sync? Editor bridge uses shared document storage + message bridge between windows. Use Reattach on the main AXIS if the popout closed uncleanly. What is Debug vs Pins? | | Debug | Pins | | --- | --- | --- | | Shows | End-of-line chips in the editor | Markers on the chart + gutter | | Needs | Line reference in logs/errors | barindex and/or bar time | | Toggle | Editor header Debug | Pins or Alt+P | Full workflow: Debugging. Multi-chart, Compare, Replay, Alerts? Layouts topbar menu: / H / V / + named recipes Compare: second symbol overlay (% or absolute) Replay: scrub loaded history (starts with full history at last bar) Alerts: local price alerts panel (webhook optional) ⌘/Ctrl+K: command palette (panels, Run, editor toggles, git) UI detail: UI shell, Charting. Why are Scripts next to the Editor? Left/right docks with multiple open panels lay out side-by-side (row), so the Scripts panel can sit left of Editor on the right strip. Bottom dock still stacks. (Internal panel id is still indicators for saved layouts.) How do I download years of history? Topbar Load is one venue page. Open the Data panel (Data Source Manager) to backfill in the background to a past date, validate completeness, and fill gaps — see Data Source Manager. Ops What is pynescript-axis? Frozen Cloudflare project name. Renaming breaks bindings and CI. Brand is AXIS; infrastructure id stays. Legacy localStorage keys? Migrated automatically into pynescript.axis.v and library keys. See State namespaces. Tests for the PWA? ``bash cd frontend && bun run test:unit bun run test:ee:smoke ` See frontend/TESTING.md`. See also Troubleshooting Glossary Compose recipes --- FILE: ../axis/docs/enduser/reference/glossary.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Glossary" description: "AXIS end-user glossary: axes, AXIS, plugins, engines, storage keys, and result terms." --- Glossary Terms as used in AXIS documentation and UI. Language runtime terms → PYNE. Product | Term | Definition | | --- | --- | | AXIS | Charting PWA product : chart, editor, panels, and store—not the language engine. | | UI track | Docs track for presentation surfaces (chart host, editor, shell, store) under /axis/docs/ui. | | PYNE | Pine Script toolchain (parse/eval); engines embed or call it. | | Active set | Tuple { source, stream, engine, storage } currently selected. | | Compose / recipe | A purposeful active set for a workflow (offline lab, desk, edge, …). | Plugin kinds | Term | Definition | | --- | --- | | Source | Historical OHLCV provider (fetchHistorical). | | Stream | Live bar/tick push (start → dispose). | | Engine | Calculator (isReady, run). | | Storage | Script library backend (list/read/write/remove). | | Component | Reserved UI slot plugins (phase ). | | Registry | In-memory catalog of plugins by kind (PluginRegistry). | | configSchema | Declarative field schema for Settings / plugin config UI. | | Capabilities | Flags: offline, needsAuth, needsNetwork, needsProxy. | Engines & backends | Term | Definition | | --- | --- | | server engine | POST {endpoint}/run with script + bars. | | pyodide engine | In-browser Python + pynescript wheels. | | Pro API / Flask | Local Python backend (make run, typically :). | | Worker | Cloudflare Worker data plane for AXIS. | | Endpoint | Base URL the server engine (and often cloud storage) calls. | Data & chart | Term | Definition | | --- | --- | | Bar | { time, open, high, low, close, volume? } | | Pane | Chart strip: price, volume, indicator, equity. | | Overlay | Plot drawn on price pane (meta.overlay !== false). | | Sub-pane | Non-overlay indicator strip; stable store id indicator. | | User drawing | Interactive annotation tool geometry. | | Script drawing | Engine-emitted line/label/box objects. | | Marker | Trade/event marker on series. | | Pane badge | Corner chip with script actions (settings / eye / re-run / remove). | | Multi-chart | Grid layouts ( / H / V / ) with per-slot symbol/interval. | | Bar Replay | Scrub/play over loaded OHLCV without live stream. | | Compare | Second-symbol series overlaid on the price pane. | | Volume profile | OHLCV volume-at-price histogram (Layers). | | Watchlist | Side panel of symbols + quote refresh. | | Scripts panel | Applied indicators/strategies list (UI title Scripts; internal id indicators). | | Layers | Panel listing panes, scripts, and user drawings. | | Data Source Manager | Background multi-page OHLCV backfill to a past date + gap validation (Data panel). | | Bars cache | IndexedDB OHLCV store keyed by source/symbol/interval for the manager. | | Chart theme preset | Named token set (void, classic, mono, obsidian, …) for chart + chrome accents. | | Alert | Local price condition (+ optional webhook); Alerts panel. | Editor & debug | Term | Definition | | --- | --- | | Debug | Inline end-of-line chips from last-run logs with line refs. | | Pins | Chart markers + gutter for logs with barindex/time. | | Problems | Clickable list of engine diagnostics for the last run. | | Ruler | -character column guide in the Pine editor. | | Symbols | Editor catalog of TV-editor-safe arrows, box drawing, and chart emoji. | | Command palette | ⌘/Ctrl+K jump menu for panels, Run, toggles, git. | | Git bar | Pull/Push via active storage plugin (local/cloud/git). | Results | Term | Definition | | --- | --- | | lastRun | In-memory last engine payload. | | Event | Strategy/order/plot-adjacent event from engine. | | Closed trade | Paired entry/exit with PnL in the viewer. | | Equity curve | Cumulative PnL series derived from trades/events. | | Scriptlogs | Floatable panel of Pine log. from last run (not System Logs). | Persistence | Term | Definition | | --- | --- | | pynescript.axis.v | Primary localStorage app state key. | | pynescript.axis.v | Older app-state key; migrated on read. | | pluginsConfig | Per-plugin configuration map. | | Library export | JSON dump of storage documents. | Infrastructure names | Term | Definition | | --- | --- | | pynescript-axis | Frozen CF Wrangler/Pages project id. | | pynescript-axis-worker | Health JSON service identity. | | void | Brand pack / dark chrome aesthetic for AXIS docs/UI. | See also FAQ State namespaces Architecture overview --- FILE: ../axis/docs/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "AXIS Documentation" description: "Open charting PWA — orthogonal sources, streams, engines, and storage. Own the axes; swap the engine." --- AXIS Documentation AXIS is an installable charting PWA (v..) that treats price, time, and calculation as separable axes. Sources load history. Streams advance the present. Engines evaluate Pine Script via PYNE. Storage keeps your script library. Compose any lawful combination without a proprietary charting host. Pine Script and TradingView are trademarks of TradingView, Inc. Cloudflare is a registered trademark of Cloudflare, Inc. This project is independent and not affiliated with or endorsed by TradingView, Inc. or Cloudflare, Inc. Abstract AXIS is a charting PWA, not a language rewrite. Language fidelity lives in PYNE. The PWA contributes: . A unified plugin registry (source | stream | engine | storage | dataset | component) . A Solid + Vite product UI (chart, editor, results, manager, Workers Manager, on-chain panel) . Optional backends: local Flask, offline Pyodide, Cloudflare Worker (+ DO/KV/D/R + /api/onchain proxy) . AXIS CLI + optional Tauri desktop shell for operators Invariant (AXIS ≠ engine): the shell never embeds a closed interpreter. Evaluation is always an engine plugin. Product map | Track | Role | Start here | | --- | --- | --- | | End User | Install, compose recipes, research workflows | End User hub | | Architecture | ADRs, topologies, state namespaces | Architecture | | Plugins | Contracts and built-in catalogs | Plugins | | UI | Chart, editor, panels, store | UI | | Worker | CF Pages + Worker data plane | Worker | | DevOps | Build, CLI, VPS, CORS, CI | DevOps · AXIS CLI | | Evaluation | Flask / Pyodide / Worker proxy — not PyneTS, not HOOX execution | Evaluation map | | PYNE (separate) | Grammar & evaluator | PYNE docs | Conceptual model Formal active set: `` Active set = { source, stream, engine, storage } Contract namespace: pynescript.axis.plugins.v App state: pynescript.axis.v On-chain datasets: kind dataset (+ source geckoterminal-ohlcv for pool candles) ` Compose in one glance | Recipe | Source | Stream | Engine | Storage | | --- | --- | --- | --- | --- | | Offline lab | mock-walk | mock-poll | pyodide | local | | Desk research | binance-rest | binance-ws | server → Flask | local | | AXIS at the edge | venue REST | venue WS / DO | server → Worker | cloud | | On-chain TVL + CEX | binance-rest + DefiLlama dataset | venue WS | any | local | | DEX pool candles | geckoterminal-ohlcv | mock-poll | any | local | | Repo library | any | any | any | git | Full recipes: Compose recipes. Infrastructure names (do not rename lightly) | Surface | Value | | --- | --- | | CF Wrangler / Pages project | pynescript-axis (frozen) | | Health JSON service | pynescript-axis-worker | | Product brand | AXIS | Demo topologies Dev: Vite : + Flask : (bun run axis:install then bun run dev) Desktop: bun run desktop:dev (Tauri ) Static: bun run build + axispwa_server.py : Worker: bun run axis dev worker / wrangler on : Prod: bun run axis:deploy → project pynescript-axis See topologies, AXIS CLI, and local dev. Offline & agent exports Auto-generated like HOOX manuals (bun run docs:exports): | Kind | Path | | --- | --- | | Full-corpus LLM pack | llm.txt | | LLM site map (llmstxt.org) | llms.txt | | Track PDFs (A) | /exports/axis--manual.pdf` | See also Quick start Plugin contracts ADRs PYNE runtime --- FILE: ../axis/docs/plugins/contracts.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Plugin contracts" description: "Formal TypeScript interfaces for AXIS source, stream, engine, storage, and component plugins (pynescript.axis.plugins.v)." --- Plugin contracts Abstract All AXIS plugins share PluginBase and a discriminant kind. Contracts live in frontend/src/plugins/types.ts. Catalogs and the dynamic loader assert these shapes before registration; the UI never calls plugins that failed assertion. Namespace (localStorage / config): pynescript.axis.plugins.v. Conceptual model Interface surface PluginKind ``ts type PluginKind = 'source' | 'stream' | 'engine' | 'storage' | 'component'; ` PluginBase | Field | Type | Notes | | --- | --- | --- | | id | string | Stable registry key within kind | | name | string | UI label | | kind | PluginKind | Discriminant | | description? | string | Manager catalog | | version? | string | Optional semver | | builtIn? | boolean | Protects against unregister | | configSchema? | ConfigSchema | Settings form | | capabilities? | PluginCapabilities | Badges: offline / needsAuth / needsNetwork / needsProxy | | init? / dispose? | lifecycle | Optional host hooks | ConfigSchema / FieldSchema Each config field: | Field | Values | | --- | --- | | type | 'string' \| 'number' \| 'boolean' \| 'select' | | default? | primitive | | label?, description?, placeholder? | UI | | min?, max?, step? | numbers | | options? | select strings | PluginContext Passed to init when used: getConfig(): Record setStatus(msg, level?) host.fetch? — injectable fetch for tests / workers pluginKey `ts pluginKey(kind, id) → ${kind}:${id} ` Used for store.pluginsConfig lookup (active.ts). --- SourcePlugin `ts interface SourceOpts { symbol: string; interval: string; limit?: number; config?: Record; } interface SourcePlugin extends PluginBase { kind: 'source'; fetchHistorical(opts: SourceOpts): Promise; searchSymbols?(query: string, config?: Record): Promise; } ` Bar shape (from store/types): { time, open, high, low, close, volume? } with time in unix seconds. Registry asserts: object, id, name, kind === 'source', fetchHistorical is a function. --- StreamPlugin `ts interface StreamOpts { symbol: string; interval: string; config?: Record; lastBar?: Bar | null; onBar: (b: Bar) => void; onError: (e: Error) => void; onStatus: (s: { state: 'open' | 'closed' | 'reconnecting' | string; url?: string; detail?: string; }) => void; } interface StreamPlugin extends PluginBase { kind: 'stream'; start(opts: StreamOpts): () => void; // stop } ` Invariant: start must return a stop function that closes sockets / clears timers. The AXIS calls stop on symbol change or unmount. --- EnginePlugin `ts interface EngineOpts { script: string; bars: Bar[]; config?: Record; signal?: AbortSignal; } interface RunResult { status: 'success' | 'error'; plots: (number | null)[]; series?: Record; events: Array; drawings?: Array>; error?: string; meta?: { mode?; scriptid?; runid?; ms?; overlay?; scriptname?; [k: string]: unknown }; } interface EnginePlugin extends PluginBase { kind: 'engine'; isReady(): Promise; run(opts: EngineOpts): Promise; } ` Notes: plots is the primary overlay series; multi-series engines also fill series. Strategy fills / orders appear in events. Pine line/label/box objects may appear in drawings from the interpret runtime. Prefer returning status: 'error' with error string over throwing for UI-friendly paths (built-in engines do both patterns carefully). --- StoragePlugin `ts interface ScriptMeta { id: string; name: string; description?: string; path?: string; updatedAt: number; createdAt?: number; revision?: string; tags?: string[]; } interface ScriptDocument extends ScriptMeta { content: string; } interface StoragePlugin extends PluginBase { kind: 'storage'; list(opts?): Promise; read(id, config?): Promise; write(doc, config?): Promise; remove(id, config?): Promise; saveDraft?(doc, config?): Promise; loadDraft?(config?): Promise; sync?(direction: 'push' | 'pull' | 'both', config?): Promise; getStatus?(config?): Promise; } ` Registry requires list, read, write, remove. Draft/sync/status are optional. --- ComponentPlugin (reserved) `ts interface ComponentPlugin extends PluginBase { kind: 'component'; slots: Array; mount(slot: string, el: HTMLElement, api: Record): () => void; } ` Registered in the registry but not yet driven by the Manager UX. Do not rely on slots in production plugins. --- Capabilities `ts interface PluginCapabilities { offline?: boolean; needsAuth?: boolean; needsNetwork?: boolean; needsProxy?: boolean; } ` Manager badges (plugin-badges.tsx) surface these for composition decisions (e.g. offline lab vs desk research). Invariants & edge cases . Export shape for dynamic modules — default export, named plugin, or the module root object (loader.asPlugin). . Config merge — catalogs merge configSchema defaults with runtime config / store.pluginsConfig. . AbortSignal — server engine honors signal / adaptive timeouts; custom engines should too for long runs. . Revision / If-Match — cloud storage uses revision strings for optimistic concurrency (Worker If-Match). Failure modes | Error | Contract violation | | --- | --- | | source: fetchHistorical() required | Missing method on register | | Unknown plugin kind | Typo or future kind not supported by loader | | Custom storage plugins via URL are not supported yet` | Dynamic storage blocked by design | See also Registry Dynamic loader Plugin examples --- FILE: ../axis/docs/plugins/datasets.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Datasets" description: "On-chain / alternate series dataset plugins — fetchDataset contract, DefiLlama TVL, GeckoTerminal DEX OHLCV." --- Datasets Abstract A dataset plugin loads non-OHLCV (or alternate OHLCV) series for the chart host (protocol TVL, DEX pool candles, funding, events). Kind is dataset on the unified registry. Phase surfaces DefiLlama protocol TVL; Phase adds GeckoTerminal pool OHLCV and derived TVL events. UI: On-Chain panel. Contract: src/plugins/types.ts (DatasetPlugin) and src/onchain/types.ts. Conceptual model Interface surface ``ts interface DatasetPlugin { id: string; name: string; kind: 'dataset'; fetchDataset(opts: DatasetFetchOpts): Promise; searchInstruments?(query: string, config?: Record): Promise; capabilities?: { needsNetwork?: boolean; needsProxy?: boolean; / … / }; } ` DatasetFetchOpts | Field | Notes | | --- | --- | | instrument | chainId, optional protocolId / address, metric, optional facet, symbol label | | resolution | e.g. d, h | | startTime / endTime | unix seconds (optional window) | | limit | optional max points | | signal | AbortSignal | | config | plugin config bag (baseUrl, …) | OnchainDataset | Field | Notes | | --- | --- | | kind | ohlcv \| scalarseries \| multiseries \| events \| table | | points / series / bars / events | payload shape depends on kind | | provenance | { provider, queryId?, url? } — required for UI honesty | | finality | pending \| safe \| finalized \| unknown | | synthetic | true when OHLC was synthesized from marks | Providers | id | Name | Network | Notes | | --- | --- | --- | --- | | defillama-tvl | DefiLlama TVL | yes | Protocol TVL history; browser uses Worker llama proxy by default | | geckoterminal | GeckoTerminal | yes | DEX pool OHLCV + pool search; browser uses Worker gecko proxy by default | Helpers (not only plugins): | Module | Role | | --- | --- | | src/onchain/defillama.ts | Protocol TVL fetch + history parse | | src/onchain/geckoterminal.ts | Pool OHLCV parse/map, search, interval/network maps | | src/onchain/events.ts | buildTvlSpikeEvents from scalar TVL points | | src/onchain/proxy.ts | resolveDefiLlamaBaseUrl / resolveGeckoTerminalBaseUrl | Worker proxy (CORS) Default bases (when plugin config.baseUrl is empty): `text {store.endpoint}/api/onchain/llama {store.endpoint}/api/onchain/gecko ` DefiLlama | Client path | Worker | Upstream | | --- | --- | --- | | /protocols | GET /api/onchain/llama/protocols | https://api.llama.fi/protocols | | /protocol/:slug | GET /api/onchain/llama/protocol/:slug | https://api.llama.fi/protocol/:slug | GeckoTerminal | Client path | Worker | Upstream | | --- | --- | --- | | /networks/:net/pools/:addr/ohlcv/:tf | GET /api/onchain/gecko/networks/.../ohlcv/... | https://api.geckoterminal.com/api/v/networks/.../ohlcv/... | | /search/pools | GET /api/onchain/gecko/search/pools | https://api.geckoterminal.com/api/v/search/pools | Allowlisted query keys: OHLCV: aggregate, limit, currency, beforetimestamp Search: query, network, page, include Address validation on the Worker: EVM x[a-fA-F-]{} or Solana-style base ^[-A-HJ-NP-Za-km-z]{,}$. Network ids: ^[a-z-_]+$. Resolver: src/onchain/proxy.ts. Handler: worker/src/onchain.ts. Cache keys (IDB dataset cache): provider|chainId|protocol|address|metric|facet|resolution via instrumentCacheKey. Related On-Chain data (end user) Worker overview — /api/onchain/…` route table Sources — OHLCV (separate plane) Contracts — shared plugin base --- FILE: ../axis/docs/plugins/dynamic-loader.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Dynamic plugin loader" description: "Install ES-module plugins from URL (source, stream, engine, dataset, component), persist, restore, safety rules." --- Dynamic plugin loader Abstract The loader (src/plugins/loader.ts) lets users install third-party or example plugins at runtime via dynamic import(). Supported kinds: source, stream, engine, dataset, component (e.g. PYNE Agent). Installed URLs persist in localStorage under: | Key | Role | | --- | --- | | pynescript.axis.plugins.v | Installed plugin list | On boot, restoreInstalledPlugins() re-imports every saved URL. Conceptual model Interface surface ``ts loadPluginFromUrl(url: string): Promise removePlugin(id: string, kind?: string): void getInstalledPlugins(): InstalledPlugin[] restoreInstalledPlugins(): Promise normalizePluginUrl(url: string): string assertSafePluginUrl(href: string): void PLUGINSKEY // 'pynescript.axis.plugins.v' ` InstalledPlugin `ts { url, id, name, kind, description? } ` Module export shapes accepted . export default plugin . export const plugin = … . Module namespace object that is the plugin Must include id and kind. Required methods / contracts: | kind | Requirement | | --- | --- | | source | fetchHistorical | | stream | start | | engine | run | | dataset | dataset fetch contract (onchain catalog) | | component | ComponentPlugin — slots / mount; optional init / dispose (registered on the unified registry) | | storage | Rejected via URL install — use built-in local/cloud/git | Internals URL normalization `ts // /src/plugins/foo.js → /plugins/foo.js href.replace(/(^|\/)src\/plugins\//, '$plugins/'); ` Dev-only Vite paths never ship in dist/; production examples live under public/plugins/ → /plugins/…. Safety assertSafePluginUrl rejects: javascript: vbscript: data:text/html… This is not a full sandbox. Dynamic plugins run with the page’s privileges (network, DOM). Treat plugin URLs like executable code supply chain. Dedup on install List filters out same URL or same (kind, id) before append — reloading an updated module replaces the entry. Unregister removePlugin calls unregisterDynamic for the resolved kind (or all kinds if unknown) and rewrites the installed list. Bootstrap coupling loadPluginFromUrl / restoreInstalledPlugins call ensureBuiltins() first so catalogs exist before dynamic ids land. Manager UX (product) From src/plugins/README.md: . Manager → Plugins → Load from URL . Example: http://localhost:/plugins/example-coingecko-source.js . Export installed / Import… for machine migration . Auto-activate patterns for source/stream/engine after install (Manager catalog Use) Shipped examples (also under public/plugins/): | File | Kind | | --- | --- | | example-coingecko-source.js | source | | example-tiny-pyne-engine.js | engine | | example-cf-do-stream.js | stream | External component example: PYNE Agent — https://pyne-agent-worker.cryptolinx.workers.dev/plugin/axis-pine-agent.js Invariants & edge cases . CORS on module URL — the JS file origin must allow module import (same-origin is easiest). . CORS on plugin’s own API — separate problem; may need proxy (see plugins README). . Partial restore — one bad URL logs error and continues others. . No code signing — trust the URL owner. Worked example `bash After bun run build + axispwaserver.py Load: http://...:/plugins/example-coingecko-source.js ` Then select CoinGecko in the source picker (id coingecko). Failure modes | Error | Meaning | | --- | --- | | URL required | Empty input | | Plugin URL scheme not allowed | Dangerous scheme | | Module did not export a plugin object | Wrong export shape | | Plugin needs id and kind | Incomplete object | | Source plugin needs fetchHistorical() | Incomplete source | | Unknown plugin kind` | Typo or unsupported kind (not in source/stream/engine/dataset/component) | | Restore log errors | , network, syntax error in remote JS | See also Plugin examples PYNE Agent plugin Registry CORS and origins --- FILE: ../axis/docs/plugins/engines.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Engines" description: "Calculation engines: server (Flask/Worker) and client Pyodide — run contracts, assets, and readiness." --- Engines Abstract An engine evaluates a script against bars and returns plots, series, events, and optional drawings. AXIS ships two built-ins in src/engines/catalog.ts: | id | Name | Where Python runs | | --- | --- | --- | | server | Server-Side | External Flask Pro API or Cloudflare Worker that proxies/evaluates | | pyodide | Client-Side (Pyodide) | Browser WebAssembly via self-hosted Pyodide | There is no third built-in engine in AXIS itself. Worker in-process Pyodide is a separate, feature-gated path on the edge (PYODIDEINWORKER) and is not production-ready — see Worker runtime. Conceptual model Interface surface ``ts isReady(): Promise run({ script, bars, config?, signal? }): Promise ` RunResult is defined in contracts. server engine Config schema | Key | Default | Notes | | --- | --- | --- | | endpoint | http://localhost: | Overridden by topbar / store.endpoint | | mode | interpret | interpret \| compile | HTTP contract POST ${endpoint}/run?mode=… Body: { script, data: bars } Success JSON: plots, series?, events?, drawings?, meta?, mode, scriptid, runid Error: non-OK status or status: 'error' → mapped to RunResult with error WebSocket contract (preferred when available) URL: ws(s)://host/ws/run (derived from endpoint; requires flask-sock on the Pro API) Client → { type: "run", id, script, data, mode? } Server → { type: "result", id, status: "success", plots, series, … } or { type: "error", id, message } AXIS server engine tries WS first (preferWs, default true), then REST. Health GET / reports "websocket": true when the route is mounted. meta.transport is "ws" or "rest" so the Connection HUD can show the path used. Readiness: GET ${endpoint}/ within s. Timeouts: adaptive min(s, max(s, s + bars×ms)). Note on Worker path: the PWA’s server engine posts to /run on whatever endpoint you set. The Worker exposes POST /api/run. When pointing the PWA at a Worker, either put a reverse-proxy path rewrite in front, or set the endpoint so the engine path matches your deployment. Worker handleRun validates { script, data, mode? } and today prefers proxying to EXTERNALBACKEND (Flask). In-worker Pyodide only if PYODIDEINWORKER=enabled and the wheel pipeline works. pyodide engine Config | Key | Default | | --- | --- | | indexUrl | /pyodide/v../ (self-hosted) | Boot pipeline (ensure) . Prefetch assets (prefetchPyodideAssets) — wasm, stdlib zip, micropip wheels. . loadPyodide({ indexURL }). . micropip.install same-origin /vendor/pynescript-..-py-none-any.whl and antlr runtime wheel. . Fetch /pyodide/pynescriptruntime.py and runPythonAsync it. Refreshing the pyne wheel (after pyne compiler/runtime changes): `bash from axis repo — builds sibling ../pynescript (or PYNEROOT) and vendors the wheel ./scripts/sync-pyne-wheel.sh bun run build ` Keep the hard-coded wheel filename in src/engines/catalog.ts / index.js in sync if the package version changes. The browser bridge is interpret-first; mode=compile/auto uses the wheel’s pynescript.compiler when NumPy loads (object-mode works without Numba; pure-numeric compile still needs server/Numba). . run calls runscript(script, bars) in Python and JSON.parses the result. Guards: assertZipAsset rejects HTML SPA fallbacks (classic deploy footgun when public/vendor is missing from dist/). Capabilities: { offline: true, needsNetwork: false } after assets are cached same-origin. Helpers: preloadPyodide(), LOCALPYODIDEVERSION = '..'. Internals | Path | Role | | --- | --- | | frontend/src/engines/catalog.ts | serverEngine, pyodideEngine, registration | | frontend/src/indicators/runner.ts | UI run orchestration | | frontend/public/vendor/.whl | Shipped wheels | | frontend/public/pyodide/ | Self-hosted Pyodide + runtime py | | frontend/worker/src/runtime.ts | Edge /api/run | | frontend/worker/RUNTIME.md | In-worker Python plan | Invariants & edge cases . AXIS never imports pynescript Python except through engines. . Abort — pass signal from UI cancel; server engine respects it. . Error as data — both engines often return status: 'error' instead of throwing so the Results panel can show messages. . Pyodide size — ~MB self-hosted index; first ready can take seconds; preload on idle. Worked examples Desk research (Flask) Active engine: server Endpoint: http://...: Backend: make run (Flask Pro API) Offline lab Source mock-walk, stream mock-poll, engine pyodide, storage local Requires dist/ (or Vite public) to serve /pyodide/ and /vendor/ Tiny non-Python engine See plugin examples — example-tiny-pyne-engine.js implements a JS DSL with sma / ema / rsi for demos without PYNE. Failure modes | Symptom | Cause | | --- | --- | | pynescript wheel returned HTML | SPA fallback; deploy vendor into dist; static server must not rewrite .whl | | loadPyodide not available | Missing pyodide.js / blocked script | | HTTP error from server | Flask down; wrong endpoint; CORS | | Worker NOBACKEND | EXTERNALBACKEND` empty and Pyodide path disabled/failed | See also Evaluation map — Flask vs Pyodide vs Worker vs (not) PyneTS Worker runtime Build and serve PYNE runtime PyneTS — TS library; AXIS does not import it AXIS and HOOX --- FILE: ../axis/docs/plugins/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Plugins" description: "AXIS unified plugin system: source, stream, engine, storage, dataset — contracts, registry, catalogs, and dynamic ES-module loading." --- Plugins Abstract AXIS is a plugin-composed charting PWA. Historical bars, live ticks, Pine evaluation, the script library, and on-chain datasets are not hard-coded product features — they are interchangeable modules that share one contract namespace: `` pynescript.axis.plugins.v ` The shell (AXIS) never embeds a closed interpreter. Evaluation is always an engine plugin. Market candles arrive through source and stream. Alternate series (protocol TVL, …) use dataset. Persistence goes through storage. Compose any lawful active set without a proprietary charting host. Conceptual model | Kind | Role | Required surface | | --- | --- | --- | | source | Historical OHLCV | fetchHistorical(opts) → Bar[] | | stream | Live bar updates | start(opts) → stop() | | engine | Pine / DSL evaluation | isReady(), run(opts) → RunResult | | storage | Script library | list / read / write / remove | | dataset | On-chain / alternate series | fetchDataset(opts) → OnchainDataset — Datasets | | component | UI slots (phase + URL agents) | mount(slot, el, api) — e.g. PYNE Agent | Built-ins register at bootstrap (frontend/src/plugins/bootstrap.ts). Dynamic plugins install from URL (loader.ts) and persist under localStorage key pynescript.axis.plugins.v. Interface surface | Module | Path | | --- | --- | | Contracts | frontend/src/plugins/types.ts | | Registry | frontend/src/plugins/registry.ts | | Bootstrap | frontend/src/plugins/bootstrap.ts | | Active resolution | frontend/src/plugins/active.ts | | Dynamic loader | frontend/src/plugins/loader.ts | | Public barrel | frontend/src/plugins/index.ts | | Source catalog | frontend/src/sources/catalog.ts | | Stream catalog | frontend/src/streams/catalog.ts | | Engine catalog | frontend/src/engines/catalog.ts | | Storage catalog | frontend/src/storage/catalog.ts | Built-in inventory (quick) | Kind | Built-in IDs | | --- | --- | | Sources | binance-rest, okx-rest, bybit-rest, coinbase-rest, mock-walk, csv-upload | | Streams | binance-ws, okx-ws, bybit-ws, coinbase-ws, kraken-ws, mock-poll | | Engines | server, pyodide | | Storage | local, cloud, git | Defaults when selection is missing (active.ts): source binance-rest, stream binance-ws, engine server, storage local. Invariants . AXIS ≠ engine — the UI never ships a private Pine runtime; engines are plugins. . Registry is the single source of truth — catalogs write into PluginRegistry; UI lists come from listSources() / etc. . Built-ins resist casual unregister — unregister(id) returns false for builtIn: true unless allowBuiltIn: true. . Config keys — prefer pluginKey(kind, id) → ${kind}:${id} (e.g. engine:server). . Storage via URL is rejected — dynamic loader refuses kind: 'storage' until a hardened path exists. . Install list persistence — installed plugins are stored under pynescript.axis.plugins.v. Compose recipes (plugin view) | Recipe | Source | Stream | Engine | Storage | | --- | --- | --- | --- | --- | | Offline lab | mock-walk | mock-poll | pyodide | local | | Desk research | binance-rest | binance-ws | server → Flask | local | | AXIS at the edge | venue REST | venue WS / DO | server → Worker | cloud | | Repo library | any | any | any | git | Failure modes | Symptom | Likely cause | | --- | --- | | Empty source dropdown | ensureBuiltins() never ran at app entry | | Dynamic plugin vanishes after reload | URL or CORS; check System Logs (plugins channel) | | Engine “not ready” | Flask/Worker down (server) or missing /vendor/.whl / /pyodide/ (pyodide) | | Cloud library | Missing Bearer pn… key or unbound API_KEYS KV in production | PYNE Agent (sister worker) Natural-language script authoring is not an engine — it is an optional component plugin backed by pyne-agent-worker (Cloudflare Workers AI). Install from: `text https:///plugin/axis-pine-agent.js `` Works standalone (no pyne-worker). Full guide: PYNE Agent plugin · End-user. See also Contracts Registry Sources · Streams · Engines · Storage Dynamic loader Plugin examples Worker (cloud storage + stream relay) --- FILE: ../axis/docs/plugins/pyne-agent.mdx Copyright (C) - jangoblockchained This file is part of AXIS. SPDX-License-Identifier: AGPL-.-only --- title: "PYNE Agent plugin" description: "Install the pyne-agent-worker AXIS component plugin — natural-language PYNE script chat via Cloudflare Workers AI (standalone or HOOX-enhanced)." --- PYNE Agent plugin Abstract pyne-agent-worker is a sister Cloudflare Worker that turns natural language into PYNE-compatible scripts. AXIS consumes it as a component plugin (kind: 'component') loaded from URL. It does not replace engines — evaluation still goes through your active engine (server, pyodide, …). The agent only writes script source into the editor / chat UI. | | | |--|--| | Repo | hoox-sh/pyne-agent-worker | | Plugin module | GET /plugin/axis-pine-agent.js | | API | POST /v/chat | | Standalone | Workers AI only — no pyne-worker / HOOX mesh required | | Optional | pyne-worker validate→retry when configured | Pine Script and TradingView are trademarks of TradingView, Inc. Cloudflare is a registered trademark of Cloudflare, Inc. Independent project — not affiliated with TradingView, Inc. or Cloudflare, Inc. Conceptual model Install in AXIS . Deploy pyne-agent-worker (standalone is enough). . AXIS → Manager → Plugins → Install from URL: ``text https://pyne-agent-worker.cryptolinx.workers.dev/plugin/axis-pine-agent.js ` (Or your own deploy: https:///plugin/axis-pine-agent.js.) . Configure plugin fields: | Field | Meaning | | --- | --- | | endpoint | Worker origin (no trailing slash) | | apiKey | Worker APIKEY (optional in open local mode) | | pineVersion | auto \| v \| v | | style | auto \| indicator \| strategy \| library | . Open the agent from the manager tab / topbar action, Workers Manager → Install/Configure, or the floating PYNE Agent button when mounted. Component URL load is enabled (AXIS ..x): loadPluginFromUrl accepts kind: 'component', registers via registry.registerComponent, and runs optional init({ getConfig }). Contract namespace remains pynescript.axis.plugins.v. Export shape: default ES module with kind: 'component', slots, mount, optional init/dispose. See also: Dynamic loader, Plugin examples, end-user guide PYNE Agent, Workers Manager. Standalone vs HOOX | Mode | Config | Behavior | | --- | --- | --- | | Standalone (default) | No PYNESERVICE / PYNEWORKERURL on the worker | Chat + optional RAG; validation.skipped | | HOOX-enhanced | Optional pyne-worker binding or URL | generate → POST /run → fix retries | AXIS users who only want natural-language script authoring never need to deploy pyne-worker. API surface (worker) | Method | Path | Role | | --- | --- | --- | | GET | /health | mode: standalone \| hoox | | POST | /v/chat | NL → reply + extracted pine | | GET | /plugin/axis-pine-agent.js | This plugin module | | GET | / | Built-in chat shell (iframe / demo) | Auth: X-API-Key or Authorization: Bearer when the worker secret is set. Legal / content The agent repo never ships TradingView built-in indicator sources. Knowledge (docs v/v, open corpus ≤ , operator builtin refs) is ingested privately into R + Vectorize. Always treat marks: Pine Script, TradingView, Cloudflare. Failure modes | Symptom | Likely cause | | --- | --- | | Plugin load fails (kind: component) | CORS / mixed content on module URL; verify HTTPS and that AXIS is current (component URL load is supported) | | on chat | Missing/wrong apiKey vs worker APIKEY | | Empty RAG quality | KB not ingested; chat still works with model knowledge only | | validation.available: false` | Expected in standalone — not an error | See also Manager and settings End-user: PYNE Agent pyne-agent-worker README PYNE docs --- FILE: ../axis/docs/plugins/registry.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Plugin registry" description: "PluginRegistry singleton: ordered maps per kind, listeners, built-in protection, and bootstrap wiring." --- Plugin registry Abstract PluginRegistry (frontend/src/plugins/registry.ts) is the single in-memory source of truth for every installed plugin of every kind. Catalogs, the dynamic loader, active resolution, and Manager UI all read/write through this class (or thin wrappers that call it). Conceptual model Interface surface Singleton ``ts import { registry, PluginRegistry } from './plugins/registry'; // or from './plugins' barrel ` Per-kind API (pattern) | Method | Behavior | | --- | --- | | registerSource(p) | Assert contract; set map; append order if new; emit registered | | getSource(id) | Map lookup | | listSources() | Registration order, not insertion-hash order | | unregisterSource(id, { allowBuiltIn? }) | Refuses builtIn unless opted in | | Same pattern for stream / engine / storage | | | registerComponent / listComponents | Phase- components (unordered Map values) | Bulk | Method | Behavior | | --- | --- | | register(plugin: AnyPlugin) | Dispatch on kind | | unregister(kind, id, opts?) | Kind switch | | clear() | Wipe all maps and order arrays (tests) | | summary() | { sources, streams, engines, storages } lightweight summaries | | on(listener) | Subscribe to { type, kind, id }; returns unsubscribe | Order preservation Private arrays sourceOrder, streamOrder, engineOrder, storageOrder ensure UI dropdowns stay stable when plugins re-register (idempotent set without reordering). Built-in protection `ts if (p.builtIn && !opts?.allowBuiltIn) return false; ` Dynamic uninstall paths must never silently delete binance-rest / server / local. Internals (repo paths) | Concern | Path | | --- | --- | | Class + singleton | frontend/src/plugins/registry.ts | | Idempotent builtins | frontend/src/plugins/bootstrap.ts → ensureBuiltins() | | Active set | frontend/src/plugins/active.ts | | Dynamic register helpers | sources/catalog.ts registerDynamicSource, etc. | Bootstrap `ts // bootstrap.ts — once per page lifetime ensureSourcesRegistered(); ensureStreamsRegistered(); ensureEnginesRegistered(); ensureStoragesRegistered(); ` Safe to call repeatedly: catalogs use a local registered flag; bootstrap uses done. Active resolution defaults | Slot | Default id | | --- | --- | | source | binance-rest | | stream | binance-ws | | engine | server | | storage | local | Fallbacks chain: store.activePlugins. → legacy store fields → default → registry get with hard fallback id → throw if still missing (source/stream/engine). Storage returns undefined if nothing registered (should not happen after builtins). Engine endpoint surface getActiveEngineConfig() injects store.endpoint when the engine has an endpoint config field or id is server — keeps the topbar Endpoint field and engine config aligned. Invariants & edge cases . Re-register same id — overwrites the plugin object; does not duplicate order entry. . Listener errors are swallowed — one bad subscriber cannot break registration. . clear() does not reset bootstrap/catalog flags — tests use resetBootstrapFlag / _resetRegistrationFlag helpers. . Components — no order array; listComponents is Map iteration order. Worked examples Register a test source `ts import { registry } from '../plugins/registry'; registry.registerSource({ id: 'fixture', name: 'Fixture', kind: 'source', async fetchHistorical() { return [{ time: , open: , high: , low: , close: }]; }, }); ` Listen for dynamic installs `ts const off = registry.on((ev) => { if (ev.type === 'registered' && ev.kind === 'engine') { console.log('engine ready', ev.id); } }); // later: off() ` Failure modes | Symptom | Cause | | --- | --- | | source: kind must be 'source' | Wrong discriminant on register | | Dropdown empty after clear() in test | Forgot re-ensureBuiltins` / reset flags | | Unregister returns false | Built-in protection | See also Contracts Dynamic loader Plugins hub --- FILE: ../axis/docs/plugins/sources.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Sources" description: "Built-in historical data sources: venue REST, mock walk, CSV upload — contracts, config, and fallbacks." --- Sources Abstract A source loads a finite window of OHLCV bars for the chart and engine. Sources live in src/sources/catalog.ts and register into the unified registry via ensureSourcesRegistered(). Conceptual model Built-in catalog | id | Name | Network | Notes | | --- | --- | --- | --- | | binance-rest | Binance REST | yes | Public klines; synthetic walk fallback when fallback: true (default) | | okx-rest | OKX REST | yes | Candles; symbol BTCUSDT → BTC-USDT; max bars | | bybit-rest | Bybit REST | yes | Spot v klines; newest-first reversed | | coinbase-rest | Coinbase REST | yes | Exchange candles; USDT pair rewritten to USD product | | mock-walk | Mock Walk | no | Offline random walk; optional Mulberry-like seed | | csv-upload | CSV / JSON Upload | no | Reads last upload from upload-store | UI order is BUILTINSOURCES array order (Binance first). Interface surface Every source implements SourcePlugin (contracts): ``ts fetchHistorical({ symbol, interval, limit?, startTime?, // unix seconds (optional) endTime?, // unix seconds (optional) — walk-back pagination signal?, // AbortSignal for background jobs config?, }): Promise ` One-shot Load vs Data Source Manager | Path | Behavior | | --- | --- | | src/data/load-symbol.ts | Single fetchHistorical (limit from Settings historyBars) → chart | | Data Source Manager | Multi-page walk-back with endTime only per page, then validate + gap-fill, durable IDB cache | Do not pass startTime + endTime together when paginating on Binance-style venues: they return the first N bars from startTime, which can falsely complete a multi-year job in one page. Config highlights binance-rest | Key | Default | Meaning | | --- | --- | --- | | baseUrl | https://api.binance.com | Override for mirrors/proxies | | limit | | – | | fallback | true | On network error, synthesizeWalk | mock-walk | Key | Default | Meaning | | --- | --- | --- | | seed | | = non-deterministic; non-zero = deterministic PRNG | | startPrice | | Walk origin | | limit | | Bar count | csv-upload No schema fields. User must Upload a CSV (time,open,high,low,close[,volume]) or JSON array first; otherwise throws a clear error. Internals | Path | Role | | --- | --- | | src/sources/catalog.ts | Definitions + register/list/get + sourcePageLimit | | src/sources/upload-store.ts | In-memory last upload for csv-upload | | src/data/load-symbol.ts | One-shot Load pipeline | | src/data/data-source-manager.ts | Background backfill + validate + gaps | | src/data/bars-cache.ts | IDB OHLCV cache for manager | | src/data/bars-gaps.ts | Coverage / gap detection | | src/data/parse-bars.ts | CSV/JSON normalization | Interval → venue codes Helpers map AXIS intervals (m, m, m, h, h, d, w) to OKX bar, Bybit interval codes, Coinbase granularity seconds. Unmapped intervals fall back to daily-ish defaults — check venue docs if you add exotic TFs. Dynamic registration `ts registerDynamicSource(plugin) // from catalog / loader unregisterDynamicSource(id) listDynamicSourceIds() ` Loader path: kind === 'source' + fetchHistorical required. Invariants & edge cases . Time unit — always seconds unix (Binance ms ÷ ). . Binance fallback — offline demos “work” but are not real prices; disable fallback for strict desk research. . CORS — browser → public venue APIs must allow CORS; corporate proxies may require a Worker /api/proxy (not shipped as a general proxy today — plan carefully). . Newest-first venues (OKX, Bybit, Coinbase) — catalog reverses/sorts ascending by time for the chart. Worked example `js // Minimal custom source (ES module for loader) export default { id: 'static-two', name: 'Two Bars', kind: 'source', async fetchHistorical() { return [ { time: , open: , high: , low: ., close: ., volume: }, { time: , open: ., high: , low: , close: ., volume: }, ]; }, }; ` Failure modes | Error / symptom | Fix | | --- | --- | | No uploaded file… | Use Upload before selecting csv-upload | | Empty chart + Binance down | Expected synthetic walk if fallback on | | OKX code !== ''` | Symbol format / region block | | CORS blocked | Proxy or different source | See also Streams Contracts Plugin examples (CoinGecko) --- FILE: ../axis/docs/plugins/storage.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Storage" description: "Script library backends: local (IndexedDB), cloud (Worker /api/scripts), and git (GitHub/GitLab)." --- Storage Abstract Storage plugins persist the user’s Pine library and drafts. Built-ins: local, cloud, git — registered from frontend/src/storage/catalog.ts. High-level UI/editor APIs go through frontend/src/storage/service.ts, which always dual-writes drafts to local for crash recovery. Dynamic kind: 'storage' install via URL is not supported (loader.ts throws). Conceptual model Built-in plugins local (frontend/src/storage/local.ts) | Concern | Detail | | --- | --- | | Primary | IndexedDB pynescript.axis.storage — stores scripts, kv | | Fallback | localStorage keys pynescript.axis.library.v, draft keys | | Migration | Older library keys (pynescript.axis.library.v, editor docs) | | Memory | In-process maps when neither IDB nor LS exist (tests/SSR) | | Capabilities | Offline-friendly | cloud (frontend/src/storage/cloud.ts) | Concern | Detail | | --- | --- | | Transport | fetch to Worker /api/scripts | | Auth | Authorization: Bearer (pn…) | | Config | endpoint (default http://...:), apiKey | | Concurrency | If-Match revision headers on write | | Partition | Worker hashes key → userId; rows scoped per user | git (frontend/src/storage/git.ts) | Concern | Detail | | --- | --- | | Providers | GitHub Contents API / GitLab repository files | | Config | provider, token, owner, repo, branch, basePath (default pine-library), commit template | | Commit boundary | Explicit Save only — not every keystroke | | Drafts | saveDraft / loadDraft are no-ops; local dual-write handles drafts | | Version history | listVersions / readAtRevision — GitHub/GitLab commits for the script path | | Restore | restoreScriptVersion writes historical content as a new tip commit | | Capabilities | needsNetwork, needsAuth | Interface surface Contract: StoragePlugin. service.ts API (UI-facing) | Function | Behavior | | --- | --- | | listScripts(prefix?) | Active backend list | | readScript / writeScript / removeScript | CRUD + log | | saveDraft / loadDraft | Local first; also active if supported | | exportLibraryJson / importLibraryJson | Portable backup | | getStorageStatus | Connected / dirty / remote hints | | supportsScriptVersioning | True when active storage implements git history | | listScriptVersions(id) | Commit history for a script (git only) | | readScriptVersion(id, rev) | Content at a commit SHA (git only) | | restoreScriptVersion(id, rev) | Write historical content as the new tip | Catalog helpers ``ts ensureStoragesRegistered() getStorage(id) listStorages() registerDynamicStorage(plugin) // in-process only (tests / future) unregisterDynamicStorage(id) ` Internals | Path | Role | | --- | --- | | storage/catalog.ts | Registration | | storage/local.ts | IDB + LS | | storage/idb.ts | Promise wrappers for IDB | | storage/cloud.ts | Worker client | | storage/git.ts | Orchestration | | storage/git-github.ts / git-gitlab.ts | Provider APIs | | storage/git-config.ts | Config normalize | | storage/service.ts | Facade | | worker/src/scripts.ts | Cloud API implementation | | worker/schemas/scripts.sql | D schema | D schema (cloud) `sql PRIMARY KEY (userid, id) -- scripts + scriptdrafts tables ` Without D, Worker keeps per-isolate in-memory maps (fine for wrangler dev). Invariants & edge cases . Draft recovery — never rely solely on remote drafts; service always hits local. . Git PAT scope — GitHub contents:write; GitLab api / writerepository. . Revisions — local uses local-${timestamp}; cloud/git use remote revisions for conflict detection. . Active default — getActiveStorageId() → local when unset. Worked examples Export / import `ts const docs = await exportLibraryJson(); // …move machine… await importLibraryJson(docs, { forceNewIds: true }); ` Point cloud at local Worker . bun run axis dev worker (or cd worker && bun run dev) → : . Manager → storage cloud . Config endpoint http://...:, key from /api/keys or ALLOWOPENKEYS= demo key Failure modes | Error | Cause | | --- | --- | | Cloud storage requires an API key | Empty apiKey | | NOKEY / INVALID_KEY | Auth path; see Worker auth | | on write | If-Match` revision conflict | | Git / | Token, owner/repo, or basePath wrong | | Quota errors on localStorage | Large libraries should prefer IDB (automatic when available) | See also Worker data plane Worker auth Contracts --- FILE: ../axis/docs/plugins/streams.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Streams" description: "Live bar plugins: venue WebSockets, mock poll, and Cloudflare Durable Object relay example." --- Streams Abstract A stream advances the chart’s present. It opens a connection (or timer), emits onBar updates, and returns a stop function. Built-ins live in frontend/src/streams/catalog.ts. An optional Cloudflare Durable Object relay lives as a loadable example (example-cf-do-stream.js) plus Worker SessionDO. Conceptual model Built-in catalog | id | Name | Offline | Upstream | | --- | --- | --- | --- | | binance-ws | Binance WebSocket | no | wss://stream.binance.com:/ws/{symbol}@kline{interval} | | okx-ws | OKX WebSocket | no | wss://ws.okx.com:/ws/v/business candle channels | | bybit-ws | Bybit WebSocket | no | Public linear/spot kline topics | | coinbase-ws | Coinbase WebSocket | no | Exchange WS ticker/candles path | | kraken-ws | Kraken WebSocket | no | Public OHLC channel | | mock-poll | Mock Poll | yes | s synthetic updates from lastBar | UI order: venues first, mock-poll last (BUILTINSTREAMS). Interface surface ``ts start(opts: StreamOpts): () => void ` Callbacks: | Callback | Use | | --- | --- | | onBar | OHLCV update (seconds) | | onError | Hard failure (connect / parse) | | onStatus | { state: 'open' \| 'closed' \| …, url?, detail? } | Config is usually empty for venue streams; custom streams may declare configSchema (e.g. DO endpoint). Internals | Path | Role | | --- | --- | | frontend/src/streams/catalog.ts | Built-ins + register helpers | | frontend/src/streams/binance.ts | Shared Binance helpers (if split) | | frontend/src/streams/multiplex.ts | Multi-symbol helpers | | frontend/worker/src/durable-objects/session.ts | SessionDO fan-out | | frontend/public/plugins/example-cf-do-stream.js | Loadable DO client stream | Binance kline mapping `ts onBar({ time: Math.floor(k.t / ), open: +k.o, high: +k.h, low: +k.l, close: +k.c, volume: +k.v, }); ` Open bars may update repeatedly for the same time until the kline closes — chart series should treat same-time updates as replace, not append-only. mock-poll behavior Aligns bar time to interval slots. Updates high/low/close within the open slot; advances on slot change. Immediate first tick so live mode feels responsive offline. Cloudflare DO stream (example plugin) Not built-in. After load: . Config endpoint = Worker origin (https://…workers.dev or local http://...:). . Client opens wss://…/api/stream?session=…&symbol=…&interval=…. . Worker routes to SessionDO; DO opens one Binance upstream and broadcasts raw kline JSON. Requires SESSIONS Durable Object binding (often commented in wrangler.toml until provisioned). Reconnect & status honesty Built-in venue streams use openReconnectableWs (frontend/src/streams/reconnect-ws.ts): Exponential backoff (s base, s cap, attempts default). onStatus({ state: 'reconnecting', detail }) between attempts. Hard onError only on construct failure or reconnect exhausted. Multiplex keeps stream green only after state: 'open'; starts as connecting. Optional Bar.closed (Binance k.x, OKX confirm, Bybit confirm) feeds live re-run mode bar-close (settings). Multiplex also treats bar time advance as a close for venues without a flag. Invariants & edge cases . Always return stop — stop cancels reconnect timers and closes the socket. . Symbol casing — Binance lowercases stream symbols; OKX uses BTC-USDT inst ids. . Browser WS limits — many tabs × many symbols hit connection caps; DO relay amortizes upstream sockets. . Reconnect is built-in for venue WS — mock-poll does not reconnect (timer only). Failure modes | Symptom | Cause | | --- | --- | | Stream never opens | CORS N/A for WS; check firewall / venue geo blocks | | DO stream NODO | SESSIONS` binding missing | | Silent no bars | Message shape mismatch (non-kline payloads) | See also Sources Worker durable objects Plugin examples --- FILE: ../axis/docs/reference/feature-atlas.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Feature atlas" description: "Map of AXIS product surfaces to repository paths: plugins, AXIS, worker, storage, test, legacy." --- Feature atlas Abstract This atlas is a path index for AXIS. Use it to jump from a product feature to the code that implements it. Prefer Solid product paths over legacy static shell files. Conceptual model Atlas tables Plugin system | Feature | Primary paths | | --- | --- | | Contracts | src/plugins/types.ts | | Registry | src/plugins/registry.ts | | Bootstrap | src/plugins/bootstrap.ts | | Active set | src/plugins/active.ts | | Dynamic install | src/plugins/loader.ts (source/stream/engine/dataset/component) | | Barrel | src/plugins/index.ts | | Manager UI | src/ui/PluginManager.tsx, plugin-badges.tsx | | Docs (in-tree) | src/plugins/README.md | Data plugins | Feature | Primary paths | | --- | --- | | Sources | src/sources/catalog.ts, upload-store.ts | | Streams | src/streams/catalog.ts, multiplex.ts | | Engines | src/engines/catalog.ts | | Load symbol (one-shot) | src/data/load-symbol.ts, parse-bars.ts | | Data Source Manager | src/data/data-source-manager.ts, bars-cache.ts, bars-gaps.ts, ui/DataSourceManagerPanel.tsx | | Script run | src/indicators/runner.ts | | Scripts panel | src/indicators/IndicatorPanel.tsx, IndicatorCard.tsx | | Chart themes | src/theme/presets.ts, catalog.ts, manager.ts, ui/ThemePanel.tsx | Storage / library | Feature | Primary paths | | --- | --- | | Catalog | src/storage/catalog.ts | | Local IDB | src/storage/local.ts, idb.ts | | Cloud | src/storage/cloud.ts | | Git | src/storage/git.ts, git-github.ts, git-gitlab.ts | | Service façade | src/storage/service.ts | | Library panel | src/ui/ScriptLibraryPanel.tsx | | v starter pack | examples/script-library-starter.json | UI (UI) | Feature | Primary paths | | --- | --- | | Entry | src/index.tsx, src/app.tsx | | Chart host | src/chart/ChartHost.tsx, ChartWorkspace.tsx, pane-manager.ts, pane-badge.ts, series-factory.ts | | Price-scale decimals | src/chart/price-precision.ts | | Plot style parity | src/results/plot-visuals.ts → pane-manager | | Multi-chart / recipes | src/chart/layout.ts, layout-recipes.ts, chart-registry.ts, ui/ChartLayoutMenu.tsx | | Bar replay | src/chart/bar-replay.ts, ui/BarReplayControls.tsx | | Compare / volume profile | src/chart/compare-overlay.ts, volume-profile.ts | | Drawings | src/chart/drawing-layer.ts, drawings/, pyne-drawings.ts, DrawingToolbar.tsx | | Editor | src/editor/ (CM, diagnostics, ruler, git-sync, inline-debug, symbols) | | Editor chrome | ui/EditorGitBar.tsx, ui/EditorProblems.tsx | | Alerts | src/alerts/, ui/AlertsPanel.tsx | | Command palette | ui/CommandPalette.tsx, command-registry.ts | | Docks | ui/panels/ (side-by-side left/right) | | Results | src/results/, ui/ResultsPanel.tsx, StrategyReport.tsx | | Workers Manager | src/ui/WorkersManager.tsx, src/workers/ | | Store | src/store/index.ts, types.ts | | Topbar / settings | ui/Topbar.tsx, TopbarField.tsx, SettingsDialog.tsx | | Workspace snapshot | src/storage/workspace-snapshot.ts | | Desktop shell bridge | src/desktop/, src-tauri/ | Worker | Feature | Primary paths | | --- | --- | | Router / CORS | worker/src/index.ts (pickOrigin) | | Run (auth + rate limit) | worker/src/runtime.ts, pyodideruntime.ts | | Auth / keys | worker/src/auth.ts, keys.ts | | Scripts | worker/src/scripts.ts, schemas/scripts.sql | | Git OAuth | worker/src/git-oauth.ts | | Session DO | worker/src/durable-objects/session.ts | | Config | worker/wrangler.toml.example, RUNTIME.md, README.md | Build / ops | Feature | Primary paths | | --- | --- | | Package scripts | root package.json (dev, build, test:, axis:, desktop:) | | AXIS CLI | packages/cli/ (@hoox-sh/axis-cli) | | Static server | axispwaserver.py | | Public assets | public/plugins, public/vendor, public/pyodide | | Makefile | axis / axis-, pages-deploy, docker- | | Desktop CI | .github/workflows/desktop.yml | | CI | .github/workflows/ci.yml (coverage + ee smoke) | | Coverage gate | scripts/check-coverage.mjs (scoped core ≥%) | | Harden/perf audit | docs/devops/harden-perf-audit---.md | Examples & tests | Feature | Primary paths | | --- | --- | | Example plugins | public/plugins/.js | | Pine v library starter | examples/script-library-starter.json | | Unit tests | tests/ | | Worker tests | worker/tests/ | | EE | ee/, playwright.config.ts | | Testing guide | TESTING.md | | Legacy notes | LEGACY.md | Namespaces & keys | Key / namespace | Use | | --- | --- | | pynescript.axis.plugins.v | Installed dynamic plugins | | pynescript.axis.library.v | Local library LS fallback | | pynescript.axis.storage | IDB database name | | pynescript.axis.v | App state | | pynescript.axis.v | Older app-state key (write-forward) | | Contract ns | pynescript.axis.plugins.v (conceptual) | Infrastructure constants | Name | Value | | --- | --- | | CF project | pynescript-axis | | Health service | pynescript-axis-worker | | Pyodide version | .. (self-hosted path) | See also Plugins Worker Legacy shell --- FILE: ../axis/docs/reference/legacy-shell.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Legacy shell" description: "Pre-Solid static shell vs current AXIS product path — what to ignore and what still runs." --- Legacy shell Abstract AXIS’s shipping product path is Solid + Vite. An older static shell remains in the repo for historical scripts and some Bun tests. Do not document or extend the legacy shell as the primary UI. Source of truth: frontend/LEGACY.md. Conceptual model What is legacy | Path | Role | | --- | --- | | frontend/src/main.js | Old bootstrap (chart.js, legacy-topbar.js, …) | | frontend/style.css | TV-blue-era tokens | | frontend/pyne-editor.js | Pre-Solid CM wiring (renamed from pine-editor.js) | | frontend/server.ts | Bun static server for old tree | | Root / frontend sw.js (static shell SW) | Service worker for old shell | Makefile make run-frontend still prints AXIS and runs bun run frontend/server.ts on : — useful for legacy debugging, not the Solid product path. What is current | Path | Role | | --- | --- | | frontend/src/index.tsx, app.tsx | Solid app | | frontend/src/chart/ChartHost.tsx | LWC panes | | frontend/src/ui/ | Topbar, Settings, Results, Manager, … | | frontend/public/ + bun run build | Production PWA assets | | frontend/axispwa_server.py | Serve dist for demos | Migration residue | Residue | Handling | | --- | --- | | pynescript.axis.plugins.v | Plugin install list | | pynescript.axis.library.v | Library localStorage fallback | | pynescript.axis.editor.doc | Editor document backup | | CF project pynescript-axis | Frozen infrastructure id | | Some docs/Makefile strings | Current brand is AXIS | App state namespace: pynescript.axis.v. Invariants . Do not port new features into main.js. . Prefer Solid store (src/store) over state.js. . Plugin registry is the Solid path (src/plugins/registry.ts); dual registries in old JS should be treated as dead ends. . Pages deploy should use Vite dist/, not the repo root static tree. Failure modes | Confusion | Clarification | | --- | --- | | “UI doesn’t match docs” | You may be serving the legacy shell | | Plugin manager missing | Legacy topbar lacks Solid Manager | | Tests pass, UI wrong | Unit tests may still import legacy helpers | See also Feature atlas Build and serve AXIS hub --- FILE: ../axis/docs/reference/open-capability-gaps.mdx Copyright (C) - jango_blockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Open capability gaps" description: "Research synthesis of high-demand charting, strategy, and script-host capabilities that remain scarce or incomplete in mainstream platforms—framed for AXIS product direction." sidebarTitle: "Capability gaps" --- Open capability gaps Abstract This page records a product research synthesis: roughly thirty concrete capabilities that traders, quant authors, and chart power-users still treat as missing or incomplete on mainstream charting and strategy hosts. It is not a shipped-feature checklist for AXIS and not a competitive teardown of any named vendor. Use it when prioritizing AXIS surfaces (sources, streams, engines, strategy results, debugging, data plane). Items below are durable demand themes from community threads and documented hard limits—not a public vendor roadmap with delivery status. How to read this list | Term | Meaning here | | --- | --- | | Still open | Capability is absent, plan-gated to a partial substitute, or still described as unreliable for professional use | | Not a ranking poll | No single global leaderboard exists; order is thematic, then by how often demand shows up as trade-blocking | | Status changes | Individual venues or UX affordances can ship later; verify against current product notes before treating any row as forever | Highest-pressure themes overall: true tick and order-flow data, portfolio-grade strategy tooling, live multi-bracket and automated execution, higher cloud/screener ceilings, and a real interactive debugger. --- Order flow, volume, and tick data | | Capability | Why it stays open | | --- | --- | --- | | | True tick volume under volume profiles (including reliable delta) | Profiles over the same range can disagree on buy/sell aggression; many treat minute-aggregated volume as unsuitable for professional volume work | | | Real volume footprint with aggressor-tagged prints | Synthetic or approximate footprints are still not trusted next to tick-native tools | | | Transaction-level order-flow viewing | Charts that are primarily bar/minute-based cannot show trade-by-trade flow without true tick history | Live trading, brackets, and execution | | Capability | Why it stays open | | --- | --- | --- | | | Multi take-profit / multi-bracket management on live broker routes | Multi-exit levels often exist in paper/simulators while live multi-exit brackets remain incomplete or broker-dependent | | | Direct automated live order placement from strategy scripts | Strategy engines commonly fill via a broker emulator; real exchange orders need external webhooks or bridges | | | Unified routing of strategy orders through the same trading panel path as manual/paper orders | Script strategies and discretionary trading panels are often separate stacks | Strategy model: portfolio, hedge, and fill realism | | Capability | Why it stays open | | --- | --- | --- | | | Native multi-symbol / portfolio strategy backtesting | One script run is typically bound to one chart dataset; multi-asset P&L is export-and-compare, not a portfolio engine | | | Simultaneous long + short (hedge) on the same symbol | Position models are often single-direction at a time | | | Positions in symbols other than the chart asset | Cross-asset entries from one strategy remain unsupported on many hosts | | | Full tick-level historical fill realism | Fills usually walk chart OHLC with assumed open→high→low→close (or open→low→high→close) paths; higher-resolution path reconstruction is partial and often tier-gated | | | Large trade histories without silent trim | Non–deep backtests often cap order history (older trades dropped from testers); deep modes raise the ceiling but remain product-tier features | Script runtime, quotas, and tooling | | Capability | Why it stays open | | --- | --- | --- | | | Higher unique multi-series / remote-series request ceilings | Cloud hosts hard-cap distinct series fetches per script (plan tiers only raise the number) | | | Higher total script wall-time budgets | Entire-run timeouts remain fixed per plan class | | | Higher per-bar loop budgets | Tight loop-per-bar limits reject heavy iterative logic | | | Higher data, memory, and compiled-size caps | Large libraries and data-heavy studies hit opaque resource walls | | | Large multi-symbol scanning without burning unique-request quotas | Dynamic loops still count each distinct symbol/timeframe against the unique ceiling | | | Runtime profilers (wall time, memory, compiled size) before failure | Authors discover limits by rejection or runtime error, not by measurement tools | | | Interactive step-through debugger with breakpoints | Official debugging remains logs, plots, drawings, and chart colors—not a classic debugger | Script-driven screener constraints | | Capability | Why it stays open | | --- | --- | --- | | | Multiple custom indicators per screener pass | Many screeners allow only one user script per screen | | | Indicator-on-indicator composition inside the screener | Nested studies are unsupported | | | Custom timeframes in scripted screens | Non-standard bars often unavailable | | | Higher remote-series call counts inside screener scripts | Typical caps are very small (single-digit) | | | Lookback beyond a short trailing window (e.g. hundreds of bars only) | Screen math is truncated to recent history | | | Full input-type coverage for screener-hosted scripts | Several input kinds are unsupported in the screener surface | Chart UX and market coverage | | Capability | Why it stays open | | --- | --- | --- | | | Stable, discoverable watchlist gestures (e.g. context-menu add) | Removal or relocation of common chart chrome produces sustained restore demand | | | Timely exchange / venue chart coverage when a market is popular | Coverage gaps surface as high-engagement “still missing” demand until a data feed lands | | | Sector / industry / theme heatmaps and similar multi-name overview boards | Recurring UX requests outside pure charting | Open script host vs general-purpose stacks | | Capability | Why it stays open | | --- | --- | --- | | | External data sources, HTTP, and host APIs from inside scripts | Cloud sandboxes intentionally block outbound and arbitrary I/O | | | Databases, file I/O, and machine-learning libraries inside the script runtime | Language surface is chart-bound, not a general quant stack | | | True custom UI and host programming beyond chart plots and fixed panels | Outgrow path is usually a multi-asset engine with live broker APIs outside the chart host | --- Themes for AXIS These gaps map cleanly onto AXIS axes without implying any third-party product parity: | Demand theme | AXIS lever | | --- | --- | | Tick / order-flow fidelity | Pluggable sources and streams with higher-resolution bars or trades when data exists | | Portfolio multi-symbol strategies | Multi-source strategy evaluation and results beyond single-chart P&L | | Live multi-exit and automation | Optional execution adapters / webhooks (explicit, user-owned—not silent live routing) | | Runtime ceilings and profilers | Local and Worker engines with measurable limits you control | | Interactive debugger | Step and pin tooling against the active engine (see UI debugging surfaces) | | Screener scale | Offline or self-hosted scan loops unbound by a single vendor’s cloud quota | | External data / ML / DB | Host bridges and plugins—not inventing closed chart-host APIs | AXIS remains a composition host: sources load history, streams advance the present, engines evaluate scripts, storage holds libraries. Capability work should preserve that separation. Related docs Architecture overview — composition model Strategy and results (end user) — how strategy output is surfaced today Debugging (UI) — current debug surfaces Feature atlas — path index for implementation work Research notes Synthesis date context: community and documentation signals through early–mid . “Still open” means encoded as present limits or still treated as incomplete in high-engagement demand—not items already reversed by later product notes. Engagement is qualitative across forums and social posts; it is not a weighted global survey. Plan-gated historical depth and deep-backtest order ceilings partially address older “more bars / more trades” asks; those are de-emphasized relative to portfolio, execution, order-flow, and tooling gaps. --- FILE: ../axis/docs/reference/plugin-examples.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Plugin examples" description: "Worked AXIS example plugins: CoinGecko source, Tiny Pine engine, Cloudflare DO stream — load paths and contracts." --- Plugin examples Abstract AXIS ships three loadable example plugins as ES modules under frontend/public/plugins/ (also mirrored for reference under frontend/src/plugins/example-.js). They are not built-ins; install via Manager → Plugins → Load from URL. Full narrative: frontend/src/plugins/README.md. Conceptual model Loading . Serve the PWA (Vite dev, preview, or axispwaserver.py on dist). . Open Manager → Plugins. . Paste a same-origin URL, for example: `` http://...:/plugins/example-coingecko-source.js http://...:/plugins/example-tiny-pyne-engine.js http://...:/plugins/example-cf-do-stream.js ` . Load — registry updates; pickers show the new plugin. . Persistence: localStorage pynescript.axis.plugins.v restores on next visit. You may host modules on any origin that allows module CORS (same-origin is simplest). Example catalog | File | id | kind | Purpose | | --- | --- | --- | --- | | example-coingecko-source.js | coingecko | source | Public CoinGecko marketchart → synthetic OHLC | | example-tiny-pyne-engine.js | tiny-pyne | engine | In-browser JS DSL: sma/ema/rsi/plot/strategy | | example-cf-do-stream.js | cf-do | stream | WS to Worker /api/stream SessionDO | | External: pyne-agent-worker /plugin/axis-pine-agent.js | pyne-agent | component | NL → PYNE Agent chat (Cloudflare Workers AI); docs | Paths: Served: frontend/public/plugins/ Source copies: frontend/src/plugins/example-.js --- CoinGecko source Contract: kind: 'source', fetchHistorical. Config: baseUrl (default CoinGecko v), vsCurrency (default usd). Behavior: Maps common symbols (BTC → bitcoin, …). Requests market_chart for a day window derived from interval × bar budget. Synthesizes OHLC from consecutive price points (API is not full candles). Limits: public rate limits (~– calls/min); CORS depends on CoinGecko policy. Use when: demo custom source without a key. --- Tiny Pine engine Contract: kind: 'engine', isReady, run. Behavior: Tokenizes a tiny expression language. Built-ins: close, open, high, low, volume, sma, ema, rsi, plot, strategy.entry / strategy.close. Returns RunResult with plots/events — not full PYNE fidelity. Use when: offline demos without loading ~MB Pyodide or hitting Flask. --- Cloudflare DO stream Contract: kind: 'stream', start → stop. Config: endpoint — Worker base URL (https://… or http://...:). Empty → error. URL built: ` {endpoint as wss}/api/stream?session={symbol}@{interval}&symbol=…&interval=… ` Requires: Worker with SESSIONS Durable Object bound and deployed (durable objects). Use when: many tabs share one upstream Binance kline subscription. --- Minimal authoring templates Source `js export default { id: 'my-source', name: 'My Source', kind: 'source', description: 'Shown in Manager', configSchema: { baseUrl: { type: 'string', default: 'https://example.com', label: 'Base URL' }, }, async fetchHistorical({ symbol, interval, config }) { const res = await fetch(${config.baseUrl}/bars?symbol=${symbol}&interval=${interval}); return res.json(); // Bar[] }, }; ` Stream `js export default { id: 'my-stream', name: 'My Stream', kind: 'stream', start({ symbol, interval, onBar, onError, onStatus }) { const ws = new WebSocket('wss://example.com/stream'); ws.onopen = () => onStatus({ state: 'open' }); ws.onmessage = (ev) => { const b = JSON.parse(ev.data); onBar({ time: b.t, open: b.o, high: b.h, low: b.l, close: b.c, volume: b.v }); }; ws.onerror = () => onError(new Error('ws error')); return () => ws.close(); }, }; ` Engine `js export default { id: 'my-engine', name: 'My Engine', kind: 'engine', async isReady() { return true; }, async run({ script, bars }) { return { status: 'success', plots: bars.map((b) => b.close), events: [], series: {}, meta: {} }; }, }; ` Storage plugins cannot be loaded from URL yet. Invariants & edge cases . Treat plugin URLs as executable code. . normalizePluginUrl rewrites /src/plugins/ → /plugins/ for production. . Dangerous schemes rejected (javascript:, vbscript:, data:text/html`). . After load, window may emit product-specific events (see README) for UI extensions. Failure modes | Issue | Fix | | --- | --- | | Failed to restore URL | after deploy path change | | CORS on CoinGecko | Offline mock or proxy | | DO stream errors | Bind SessionDO; set endpoint | | Tiny engine parse errors | Unsupported DSL constructs | See also Dynamic loader Contracts Worker durable objects --- FILE: ../axis/docs/reference/testing.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Testing reference" description: "AXIS unit, worker, security, coverage gate, and Playwright ee conventions with paths and commands." --- Testing reference Abstract AXIS tests are Bun-first under frontend/, with Playwright for browser smoke and a scoped coverage ratchet. Canonical human guide: frontend/TESTING.md. This page is the reference map for agents and contributors. Conceptual model Commands ``bash cd frontend bun run test unit + worker tests bun run test:unit frontend/tests only bun run test:coverage lcov + text → coverage/ bun run test:coverage:gate coverage + % scoped gate bun run test:security tests/security/ bun run test:ee:smoke Playwright @smoke bun run test:ee:critical @smoke|@critical bun run test:ee all bun run test:all gate + security ` Root: `bash bun run test:frontend bun run test:frontend:coverage make test-frontend ` Layout ` frontend/tests/ setup.ts fixtures/ helpers/ .test.ts integration/ security/ frontend/scripts/check-coverage.mjs frontend/ee/ smoke.spec.ts frontend/playwright.config.ts frontend/worker/tests/ auth.test.ts keys-.test.ts scripts.test.ts … ` Coverage policy | Item | Policy | | --- | --- | | Gate | % lines on scoped core | | Ratchet history | → → → → → | | Include | plugins, storage (minus idb), store, results, sources, streams, data, chart pure helpers + series-factory/manager-access, worker auth/keys/runtime/scripts | | Exclude from gate | drawing-layer.ts, pane-manager (unit elsewhere), runner chart apply, pyodide boot, legacy JS, Solid UI .tsx | | Full report | bun test --coverage unscoped | Conventions . import './setup' first when tests touch store/plugins/storage. . No real network — mock fetch / WebSocket. . Table-driven tests for catalog built-ins. . Plugin fixtures in tests/fixtures/plugins/. . Prefer calling handlers with fake params over full browser for unit scope. Playwright Config (playwright.config.ts): testDir: ./ee Chromium project webServer: bun run build && bun run preview -- --host ... --port baseURL: http://...: (override AXISEE_BASE) Timeout s; CI retries Smoke mocks /run and Binance. Use data-testid attributes (axis-btn-load, axis-manager, …). Security suite bun run test:security covers: Plugin URL scheme rejection Storage-via-URL rejection Poisoned localStorage Worker partition isolation If-Match Admin keys Adding a unit test . Create frontend/tests/.test.ts. . Import setup if needed. . Use fixtures under tests/fixtures/. . Run bun run test:unit and bun run test:coverage:gate`. CI linkage See CI and testing for workflow job names and nightly schedule. Failure modes | Failure | Mitigation | | --- | --- | | Coverage drop | Add tests in scoped files or deliberately exclude only with policy change | | EE flake | Mock network; single worker; stable testids | | IDB in unit | Local storage falls back to memory maps | See also CI and testing Plugin contracts Worker auth --- FILE: ../axis/docs/ui/charting.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Charting" description: "AXIS chart: ChartHost, PaneManager, series factory, drawings, crosshair sync, and trade markers." --- Charting Charting is the spatial surface for OHLCV, overlays, and annotations. Implementation is imperative under a Solid shell so lightweight-charts owns its DOM subtree. Abstract | Module | Role | | --- | --- | | ChartHost.tsx / ChartWorkspace.tsx | Solid mount; multi-slot grid; empty-state | | pane-manager.ts | Multi-pane LWC charts, time sync, overlay apply | | pane-badge.ts | Corner name + script action icons | | series-factory.ts | Candle/volume/line series helpers + palette | | chart-type.ts | Price style transforms (HA, hollow, …) | | bar-replay.ts + BarReplayControls | History scrub / play | | layout.ts / layout-recipes.ts / chart-registry.ts | Multi-chart layouts + presets | | compare-overlay.ts | Second-symbol compare series | | volume-profile.ts | OHLCV volume-at-price overlay | | drawing-layer.ts + drawings/ | User + script drawings, templates, sync helpers | | price-precision.ts | Price-scale decimals (auto or –) from symbol + bars | | results/plot-visuals.ts | Pine plot.style → LWC series kind (line, stepline, columns, area, …) | | crosshair-sync.ts | Cross-pane crosshair | | pyne-drawings.ts | Engine drawing objects → layer | | manager-access.ts | Process-wide manager/layer getters | Conceptual model Interface surface Panes store.panes: { id, type, height, order, visible, label }. | type | Typical series | | --- | --- | | price | Price series (style below) + overlay lines + markers + drawings | | volume | Histogram | | indicator | Non-overlay plots | | equity | Strategy equity curve | Defaults: price + volume. Indicator/equity created on demand. Price chart styles (store.chartType) Topbar Type select (persisted). Implementation: chart/chart-type.ts + createPriceSeries. | chartType | Series | Notes | | --- | --- | --- | | candles | Candlestick | Japanese candles (default) | | hollow | Candlestick | Hollow body on up bars | | bars | Bar | Classic OHLC bars | | heikinashi | Candlestick | Heikin-Ashi transform of OHLCV | | line | Line | Close only | | area | Area | Close with fill | | baseline | Baseline | Close vs first-bar base | Series still live under pane key candle so markers, hlines, and the drawing layer keep a stable host. Changing type swaps the LWC series and rebinds data without a full history reload. Empty / status overlay When bars.length === , ChartHost shows contextual title/sub from store.status (loading, error, running, or “Load data to begin”). Price-scale decimals Price pane labels use chart/price-precision.ts: | Mode | Behavior | | --- | --- | | auto (default) | Merge symbol heuristics (e.g. BTC→, SHIB→) with recent OHLCV significant digits | | – | Fixed display decimals + matching minMove | UI control cycles A → → … → → A on the price-pane scale chrome. Store field: priceScaleDecimals (persisted). Plot style parity Engine plot series honor Pine Script plot.style via mapPlotStyleToSeriesKind (results/plot-visuals.ts → pane-manager series factory): | Pine style (examples) | Chart series | | --- | --- | | plot.styleline / default | Line | | plot.stylestepline | Stepline (LWC WithSteps) | | plot.stylesteplinediamond | Stepline + vertex point markers | | plot.stylehistogram | Histogram (base ) | | plot.stylecolumns | Columns → LWC Histogram (base ; no column-width/gap API) | | plot.stylearea / areabr | Area | | plot.stylecircles | Circular point markers + faint hairline | | plot.stylecross | Larger point markers, connector hidden | | plot.stylelinebr / steplinebr | Broken line / stepline variants (whitespace gaps) | LWC limits: Histogram series has no bar-width or gap option, so columns and histogram share the same bar geometry (both zero-based). Point markers are always circular — cross is approximated by discrete markers without a connector; steplinediamond uses stepline + vertex markers. Sparse plotshape styles map diamond/cross to square markers (optional + / ✕ glyphs when text is omitted). fill(plot, plot, color=…) renders as SVG bands between the two series when both resolve on the same pane. Multi-chart layouts Modes: / H / V / (store.chartLayout) Per-slot symbol/interval via registry; active slot drives topbar + Run Named layouts save/load (optional chrome snapshot) Recipes in Layouts menu: Scalp m+m, Swing HTF, Quad, BTC majors (layout-recipes.ts) Bar Replay Topbar Replay → session over loaded OHLCV (bar-replay.ts): Enters with full history visible (cursor on last bar) Scrub / step / play; Play at end restarts from bar Mutually exclusive with Live Compare & volume profile Compare control: second symbol as % or absolute overlay (compare-overlay.ts) Volume profile toggle in Layers → OHLCV approximation histogram (volume-profile.ts) Pane badges Each pane’s top-left chip (pane-badge.ts): label + script actions when scripts are applied: | Icon | Action | | --- | --- | | Settings | Script inputs modal | | Eye | Show / hide series | | Refresh | Re-run script | | Trash | Remove from chart | Volume/equity without scripts: name + hide. Drawings toolbar See Drawings. Tools include cursor, hline, vline, trend, ray, extend, rect, ellipse, arrow, fib, measure, text. Templates and duplicate helpers live under Layers / drawings modules. Crosshair & time syncTimeScales / syncCrosshair keep multi-pane navigation coherent. scrollToTime / jumpToDebugPin used from Results and debug pins. Internals Solid vs imperative boundary ``text // ChartHost — Solid owns outer shell // PaneManager only mutates panesEl — never put Solid children inside panesEl ` On cleanup: destroy drawing layer, dispose manager, clear accessors. Data path setDataToChart(bars) applies OHLCV; createEffect on store.bars re-applies if bars change outside load helper. Run apply (chart side) From runAndApply (indicators): syncOverlayLines — update existing overlay series in place (avoids destroy→blank→add flash on live re-runs) Markers from normalized events (set in place) Script drawings atomic replace via DrawingLayer (setScriptDrawings without empty frame); linefill quads and force_overlay geometry route with other script drawings; barcolor tints candles via plot-visuals (not the SVG layer) Equity series when strategy report warrants (silent live runs skip hide thrash) History vs live bars Full loads: loadBars → chartDataGen++ → setDataToChart + fitContent Live: multiplex → appendBar + manager.appendBar only (no fitContent, no full setData) Performance (..x): live tick path is O() append + conflation under load; heavy history paint is batched; load requests use AbortController so symbol switches cancel in-flight venue fetches Overlay re-runs prefer in-place series updates (syncOverlayLines) to avoid destroy→blank flash series-factory Centralizes series construction options, plot-style mapping, and color palette so Results plot summary and chart stay chromatically related. Invariants . Solid reconciliation must not rewrite pane roots. . Script drawings ephemeral per run; user drawings store-backed. . Overlay routing: engine meta.overlay + script type; oscillator-scale series forced to sub-pane when they would vanish on price (see Indicators). . Shared non-overlay sub-pane id is stable: indicator. . Manager may be null before mount—runner no-ops chart apply safely. Failure modes | Symptom | Cause | | --- | --- | | Blank panes after hot reload | Manager disposed; full remount | | Overlays stack forever | Old path skipped removeOverlays | | Fib/tools ignore clicks | No bars / time scale; wrong tool | | Markers at t= | Event times not bar-aligned | Worked example (dev) `ts import { getManager } from './chart/manager-access'; getManager()?.scrollToTime(barTime); `` See also Indicators Results and strategy End-user drawings --- FILE: ../axis/docs/ui/debugging.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Debugging" description: "Scriptlogs, Profiler, Debug chips, chart Pins, diagnostics, and System Logs." --- Debugging AXIS separates script-authored diagnostics (Pine log., engine errors, line timing) from shell telemetry (load, stream, engine connection). Abstract | Surface | Source of truth | Open / toggle | | --- | --- | --- | | Scriptlogs | store.lastRun → normalizePyneLogs | Editor header Scriptlogs | | Profiler | profilerEnabled + profile.lines | Editor header Profiler | | Debug | inlineDebugEnabled + last-run logs with line | Editor header Debug | | Pins | debugPinsEnabled + logs with barindex / time | Editor header Pins · Alt+P | | Diagnostics / Problems | Engine errors + line-mapped logs | Underlines + Problems list (always from last run) | | System Logs | store.logs ring buffer | Status bar / logs strip | | Module | Role | | --- | --- | | ui/ScriptLogsPanel.tsx | Floatable last-run log. list | | results/pyne-logs.ts | Normalize / filter / TSV | | results/profiler.ts | Line % profile | | results/inline-debug.ts | Annotations + pinable helpers | | results/debug-pins.ts | Bar pins, markers, countDebugPins | | editor/inline-debug.ts | CM chips, pin gutter, line flash | | editor/diagnostics.ts | Error underlines / gutter / jump | | ui/EditorProblems.tsx | Problems list UI | | chart/manager-access.ts | applyDebugPinsToChart, jumpToDebugPin | Conceptual model Invariant: Scriptlogs / Debug / Pins never write into store.logs. --- Debug vs Pins | | Debug | Pins | | --- | --- | --- | | Question | What happened on this source line? | Which bar was that on the chart? | | Requires | Line ref (line :, structured line, …) | barindex and/or time / bartime | | UI | End-of-line chips + optional level gutter | Chart circle markers + editor gutter | | Click | Pin-able chips jump if bar info present | Chip / / Scriptlogs → chart jump + line flash | | Store | inlineDebugEnabled | debugPinsEnabled | Workflow . Add log.info / log.warning / log.error (or rely on engine errors) with line and ideally bar_index. . Run. . Toggle Debug to see chips; Pins to mark bars (count shows on the Pins button). . Click a pin-able chip or → jumpToDebugPin (crosshair + scroll); source line flashes (cm-debug-pin-flash). Both can be on at once. Pins without Debug still show gutter and chart markers. --- Scriptlogs Panel for script logging from the last run. | Pine call | Panel level | | --- | --- | | log.info | info | | log.warning | warning | | log.error | error | Open: Editor → Scriptlogs (axis-btn-scriptlogs). Pin-able rows jump to the chart when bar time/index is known. --- Editor Profiler Toggle Profiler (axis-btn-profiler): Persists profilerEnabled May send profiler: true to the engine Header shows last-run ms Gutter shows line % when profile.lines is present --- Diagnostics & Problems Independent of Debug chips: structural error reporting from the run payload. Parses error, meta.errors, diagnostics, error-level logs CodeMirror underlines + severity gutter Stats strip badge (axis-editor-diag-count) and Problems list (axis-editor-problems) Click → jump to line/range --- Scriptlogs vs System Logs | | Scriptlogs | System Logs | | --- | --- | --- | | Source | Script log. on last run | AXIS shell events | | Store | lastRun | store.logs | | UI | Floatable Scriptlogs | Strip above status bar | | Control | Editor header | Status bar | --- Persistence & test ids | Key | Role | | --- | --- | | panelChrome.scriptlogs / editor | Panel chrome | | profilerEnabled | Profiler | | inlineDebugEnabled | Debug chips | | debugPinsEnabled | Chart + pin gutter | | testid | Role | | --- | --- | | axis-btn-scriptlogs | Open Scriptlogs | | axis-btn-profiler | Profiler | | axis-btn-inline-debug | Debug | | axis-btn-debug-pins | Pins | | axis-debug-pin-count | Pin count | | axis-editor-diag-count | Diagnostics badge | | axis-editor-problems | Problems panel | Unit coverage: tests/pyne-logs.test.ts, tests/profiler.test.ts, tests/inline-debug.test.ts, tests/debug-pins.test.ts, tests/editor-diagnostics.test.ts. See also Editor Charting — markers and jump UI shell --- FILE: ../axis/docs/ui/editor.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Editor" description: "AXIS Pine editor: CodeMirror , diagnostics, -col ruler, git sync, stats, docked vs popout, editor bridge." --- Editor The editor is the source axis of intent: Pine text the operator authorizes the engine to run. Completion and hover can use local builtins metadata and optional remote LSP; evaluation is always an engine plugin. Abstract | Module | Role | | --- | --- | | EditorPane.tsx | Docked/standalone chrome, header tools, detach | | tabbed-editor.tsx | Multi-tab docs, stats strip, Problems, Colors, Symbols, git bar | | PyneEditor.tsx | CM mount (extensions + view APIs) | | pyne-language.ts | StreamLanguage / highlighting (namespaces, types, multiline strings) | | pine-symbols.ts + SymbolEmojiManager.tsx | TV-editor-safe glyph catalog + insert panel | | pyne-lsp.ts + data/pyne-builtins.json | Offline completion/hover corpus | | column-ruler.ts | -character guide line | | diagnostics.ts | Underlines, gutter, jump-to from last run | | inline-debug.ts | EOL chips, pin gutter, line flash | | git-sync.ts + ui/EditorGitBar.tsx | Pull / push / storage status | | ui/EditorProblems.tsx | Collapsible problems list | | editor-bridge.ts | Cross-window doc + run messages | | cm-void.ts | Void-theme CM styling | Conceptual model Why run on main? Chart, bars, and pane manager live in the main window. Popout is a pure editing surface. Interface surface Docked mode Right dock by default (panelChrome.editor); may sit side-by-side with other right panels (e.g. Indicators left of Editor) FloatableShell: dock left/right/bottom, float, new window Detach → popup or tab (openEditorWindow) Popout mode Main shows “Editor detached” chip + Reattach Shared doc key + bridge messages: hello, doc, run, run-status, reattach, popout-opened, popout-closed Run entry points Editor / topbar Run → runAndApply(getDoc()) Popout Run → bridge → main executes Command palette: save library, git push/pull, jump to line, toggles Header tools (axis-editor-tools) Icon-only (labels in tooltips / aria-label). Open in new tab is in the left hamburger menu with dock options. | Control | Effect | | --- | --- | | Run | Run script against loaded bars | | Scriptlogs | Open last-run log. panel | | Profiler | profilerEnabled + optional line % gutter; re-run when enabling | | Debug | Inline end-of-line log/error chips (inlineDebugEnabled) | | Pins | Chart markers + gutter (debugPinsEnabled); Alt+P | | Ruler | -column guide (editorRulerEnabled) | | Hamburger → Open in new tab | Editor in a full browser tab (vs New window popup) | Details: Debugging. Stats strip Bottom of tabbed editor (data-testid="axis-editor-stats"): | Field | Meaning | | --- | --- | | Pos L:C | Cursor line:column (-based) | | Ln | Total lines in document | | Words / Chars | Document stats | | Diagnostics badge | Error/warning count from last run — click jumps to first | | Problems | Expand/collapse problems list | | Colors | Color chips / picker / converter for hex and color. | | Symbols | Insert TV-editor-safe arrows, box drawing, marks, spaces, and chart emoji (raw, quoted, or plotchar) | | wrap | Soft wrap toggle (editorWrapEnabled, default on) | Symbols & emoji Status-bar Symbols (data-testid="axis-editor-symbols") opens a searchable catalog of glyphs used in Pine Script plotchar / label.new / table text. Mono-safe — ~ cell in IBM Plex Mono / the TV editor (box drawing, ▲▼●◆✓★, arrows). Wide / chart-only — official 🠅🠇 and emoji (). Filter with Mono-safe. Click inserts at the cursor (replaces the selection). Right-click copies. Insert mode: raw glyph, quoted string, or a plotchar(cond, "mark", "…", location.belowbar) snippet. Sources: TradingView Exploring Unicode, Text and shapes, box-drawing U+, PineCoders conventions. Highlighting pyne-language.ts is a stateful stream tokenizer: //@ annotations, // and / / (including across lines), quoted and """ / ''' strings with \\n escapes, RRGGBB(AA) colors, namespaces (ta., label.new), types, control vs definition keywords, and function names before (. Format leaves continuation lines of an open string or block comment untouched. Diagnostics & Problems Pre-eval (as you type) — schedulePreeval / POST /lsp/diagnostics (when engine=server) plus local structural checks mark wrong code before Run. Severity error disables Run (topbar, editor, Mod-Enter, command palette). Warnings do not block. After a run, diagnosticsFromLastRun(lastRun, doc) still builds ranges from engine error, meta.errors / diagnostics, and error logs with line refs (line :, L:, …). Live pre-eval takes precedence when it has findings for the current buffer. CodeMirror: wavy underlines, severity gutter, hover tooltips Problems panel: clickable rows → scrollToLine / jumpToDiagnostic Unit tests: tests/editor-diagnostics.test.ts, tests/editor-problems.test.ts, tests/preevaluate.test.ts -column ruler columnRulerExtension paints a dashed guide at character column (measured via CM coords). Toggle Ruler or command palette “Toggle Editor Ruler”. Git sync bar Tab toolbar Pull / Push / status (EditorGitBar): Uses active storage plugin (local \| cloud \| git) Push → writeScript (git commits when storage is git) Pull → optional plugin sync('pull') + list/reload bound library id Config stays in Settings / Script Library (no second credentials UI) Completion corpus src/editor/data/pyne-builtins.json is synced from PYNE (scripts/sync-pyne-builtins.sh). Offline completion/hover fall back to this catalog; optional remote LSP when the Pro API is available. Library load loadLibraryDoc(doc, name) on editorRef injects storage reads into the active tab. Internals editorRef pattern App holds { getDoc, setDoc?, loadLibraryDoc?, scrollToLine?, jumpToDiagnostic?, getCursor?, insertAtCursor? } populated by CM mount—avoids full-doc store writes every keystroke (doc also mirrored to EDITORDOCKEY). Persistence | Key / field | Role | | --- | --- | | store.editor + panelChrome.editor | Dock geometry, open, mode | | pynescript.axis.editor.doc | Draft text | | profilerEnabled / inlineDebugEnabled / debugPinsEnabled / editorRulerEnabled / editorWrapEnabled | Editor tools | Invariants . Empty doc Run is a no-op at topbar/editor handlers. . Popout does not own PaneManager. . Reattach restores shared doc into docked CM. . AXIS ≠ engine: editor never fully evaluates Pine itself; pre-eval is parse/lint only. . Debug/Pins still read last run; static diagnostics also come from pre-eval. Failure modes | Symptom | Mitigation | | --- | --- | | Two editors diverge | Prefer bridge doc events; reattach | | Detached but no window | Popup blocked; allow popups; reattach | | Lost text on crash | editor.doc localStorage; library Save / git push | | Blank CM (no lines) | Dock column height chain; editor must fill strip | | No chips/pins | Enable Debug/Pins; logs need line and/or barindex | See also Debugging — Debug vs Pins, Scriptlogs, Profiler UI shell — Topbar, docks, command palette Indicators Script library --- FILE: ../axis/docs/ui/index.mdx Copyright (C) - jango_blockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "UI" description: "AXIS UI subsystem: chart host, editor, UI shell, indicators, results, and Solid store—presentation without a closed engine." --- UI UI is the AXIS browser presentation layer: everything the operator sees and clicks that is not language evaluation. Engines are plugins; AXIS applies their outputs to panes, markers, and drawers. Abstract | Subsystem | Responsibility | | --- | --- | | Charting | Panes, multi-chart, replay, compare, drawings, badges | | Editor | CM, diagnostics, ruler, git bar, stats, dock/popout | | Debugging | Scriptlogs, Profiler, Debug chips, Pins, Problems | | UI shell | Topbar groups, docks side-by-side, palette, alerts | | Scripts | Run pipeline, Scripts panel, overlay/sub-pane routing | | Results & strategy | Event normalize, trades, equity, export | | Store | Solid store, persistence, active plugins | Conceptual model Invariant (AXIS ≠ engine): no Solid component imports PYNE directly; only engine modules talk to Python/HTTP runtimes. Interface surface (visual map) ``text ┌─ Topbar (grouped: market · data · compute · layout · panels · system) ─┐ ├─ left docks ─┬─ ChartHost (– slots) + drawings ─┬─ right docks ─────┤ │ watchlist │ multi-pane + badges + replay │ indicators|editor │ │ layers … │ │ (side-by-side) │ ├─ Results / scriptlogs (bottom) ───────────────────────────────────────┤ ├─ System logs ─────────────────────────────────────────────────────────┤ └─ StatusBar (connection HUD) ──────────────────────────────────────────┘ Settings · Plugins · Command palette (⌘K) ` Implemented in src/app.tsx (repo root product UI). Stack | Concern | Choice | | --- | --- | | Framework | SolidJS . | | Bundler | Vite + vite-plugin-solid | | Charts | lightweight-charts | | Editor | CodeMirror | | Icons | lucide-solid | | Style | Tailwind + void tokens (data-theme) | Relation to other tracks Operators: End User Structure: Architecture Contracts: Plugins Language: PYNE runtime See also LEGACY.md — what is not the AXIS product path TESTING.md` — unit/ee around UI modules --- FILE: ../axis/docs/ui/indicators.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Scripts (indicators & strategies)" description: "AXIS applied-scripts UI: runAndApply pipeline, Scripts panel, overlay routing, and engine binding." --- Scripts (indicators & strategies) In AXIS, scripts applied to the chart may be Pine indicator() or strategy() definitions. The product panel is labeled Scripts (panel id remains indicators for workspace compatibility). The module boundary is indicators/runner.ts plus list UI—not the PYNE evaluator. Abstract | Piece | Role | | --- | --- | | runScript | Engine invocation + status/timeouts | | runAndApply | Chart + lastRun + results open; may addIndicator | | probeEndpoint | Settings health check helper | | Scripts panel (IndicatorPanel) | List / toggle / colors for store.scripts | | IndicatorCard | Per-script UI chrome | Language semantics: PYNE runtime. Conceptual model Interface surface runScript options | Option | Default | Meaning | | --- | --- | --- | | silent | false | Quiet status; live re-run logging | | timeout | derived | Interactive: scales with bars (–s); silent s | runAndApply options | Option | Default | Meaning | | --- | --- | --- | | openResults | !silent | Open results drawer | | indicatorId | optional | Update existing store indicator | Overlay routing ``text overlay = resolveOverlayFlag(meta.overlay, scripttype) // explicit false → sub-pane // explicit true → price // missing: indicator → sub-pane; strategy → price if overlay && seriesWouldHideOnPrice(plots, bars): overlay = false // RSI / scaled oscillators on BTC scale paneId = overlay ? 'price' : 'indicator' // stable id indicator ` Non-overlay ensures store + manager pane id indicator (not a random uid). Orphan empty indicator panes are cleaned up. After apply, sub-panes force autoScale / resize so lines paint on first run. Prefer indicator(..., overlay=false) and raw plot(rsi) for oscillators — not strategy(..., overlay=true) with rsi . on price. Series selection Prefer result.series entries whose keys do not start with / . Fallback: single plots[] array as one line named from scriptname. Colors: plotmeta[k].color or PLOTPALETTE[i]. Pine Script plot.style_ maps to LWC series kinds (line, stepline, histogram/columns, area, circles) — see Charting — plot style parity. Indicator panel & pane badges Side list of store.scripts (Indicator: id, name, code, paneId, visible, plots) Visibility toggles affect display; re-run is still engine-driven Chart pane chips mirror settings / eye / re-run / remove (see Charting) Active engine binding `ts // plugins/active.ts pattern getActiveEngine() // registry lookup by activePlugins.engine getActiveEngineConfig() // pluginsConfig + store.endpoint merge ` Switching engines mid-session does not auto-rerun; operator hits Run. Live re-run coupling Streams may set live.needsRerun; multiplex/runner path can call runAndApply with silent: true so the status bar is not spammed. Failures go to logs. Internals | Path | Role | | --- | --- | | src/indicators/runner.ts | Pipeline | | src/indicators/IndicatorPanel.tsx | List UI | | src/plugins/active.ts | Active engine/source helpers | | src/engines/catalog.ts | Built-in engines | | src/results/plot-visuals.ts | Plot style → series kind | Invariants . Runner is the only chart-facing calc orchestrator (avoid duplicate apply logic in components). . Errors still setLastRun with status: 'error' when possible. . Clearing overlays happens before re-adding—no unbounded series leak. . AXIS does not interpret Pine; it only maps RunResult`. Failure modes | Symptom | Layer | | --- | --- | | Engine exception | Results error + status | | Success empty plots | Script/meta—not runner | | Manager null | Chart not mounted; run still sets lastRun | | AbortError | Timeout—reduce bars or optimize script | See also Charting Results and strategy ADR- / ADR- --- FILE: ../axis/docs/ui/results-and-strategy.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Results and strategy" description: "UI-side event normalization, trade pairing, metrics, equity curve, and ResultsPanel export pipeline." --- Results and strategy Results UI turns opaque engine payloads into operator-consumable tables, metrics, and files. It is a pure-ish viewer layer over lastRun + bars. Abstract | Module | Role | | --- | --- | | results/events.ts | Normalize heterogeneous event shapes; markers; equity series | | results/strategy.ts | Pair trades; stats; equity SVG helpers; CSV | | ui/ResultsPanel.tsx | Tabs, export, jump-to-time | | ui/StrategyReport.tsx | Stats cards, equity curve, trades table, CSV | | ui/StatusBar.tsx | Compact trade summary | Conceptual model Event normalization Engines may emit Pro API parity fields (kind, bartime, direction, ohlc) or legacy UI fields (type, time, dir, price). Normalizer: Unifies time/price/dir/id Optionally fills price from bars when OHLC present/empty Can include order-like events for the Events tab Trade pairing algorithm Sketch (buildStrategyReport): . Sort by time. . On entry-like kind → open map by id. . On exit/close-like → match id, else sole open trade. . PnL = Δprice × long/short sign. . Aggregate win rate, profit factor, max DD on cumulative PnL. Not modeled: fees, funding, partial fills, portfolio multi-symbol—viewer honesty. ResultsPanel tabs | Tab | Data source | | --- | --- | | Events | normalizedEvents | | Strategy | report.trades + report.stats | | Plots | series / plots summary | | Metrics | meta + context + stats subset | | Raw | JSON.stringify(lastRun) | Interactions: copy, export, scrollToTime on trade rows. Scriptlogs / Debug Pins also jump the chart (jumpToDebugPin) when bar time/index is present — see Debugging. Equity buildEquityCurve feeds chart equity pane when runner/strategy path requests it—derived series, not engine-native unless also provided. Export (ADR-) | Artifact | Function | | --- | --- | | axis-run-.json | Full payload | | axis-trades-.csv | tradesToCsv | | Clipboard | CSV when permitted | Invariants . Results recompute from lastRun reactively; they do not re-query engines. . Changing bars without re-run can change normalization fills—re-run after Load. . lastRun not rehydrated from localStorage (session artifact). . Infinite profit factor when no losses but profits exist—display must tolerate non-finite. Failure modes | Symptom | AXIS explanation | | --- | --- | | Events > , trades = | Unpaired or missing prices | | CSV disabled | No closed trades | | Jump no-ops | Manager unmounted / invalid time | Internals cross-links Operator narrative: Strategy and results. Architecture: ADR-. See also Indicators Charting --- FILE: ../axis/docs/ui/store.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Store" description: "AXIS Solid store: AppState shape, activePlugins, persistence rules, logging, and alignment helpers." --- Store The Solid store is the control-plane nexus of AXIS. Plugins read configuration from it; UI binds reactively; persistence snapshots a safe subset to pynescript.axis.v. Abstract | Export | Role | | --- | --- | | store / setStore | createStore | | persist | Debounced localStorage write | | setActivePlugin | Kinded selection + flat field mirrors | | appendLog / status helpers | Operator-visible telemetry | | STORAGEKEY | pynescript.axis.v | Types: frontend/src/store/types.ts. Conceptual model AppState map | Field | Persistence | Notes | | --- | --- | --- | | bars | no | Session history | | symbol, interval, exchange | yes | Market context | | source, engine | yes | Flat mirrors | | endpoint | yes | Server engine base URL | | activePlugins | yes | Canonical four-tuple | | pluginsConfig | yes | Per-plugin fields | | scripts, panes | yes | Indicator list + layout | | live | partial | streamId important | | theme | yes | dark/light | | editor, watchlist, panels | yes | Chrome geometry | | stream.status | yes/volatile | Connection hint | | status, statusMessage, lastRunMs | yes | UX; ok to restore | | lastRun | no | Forced null on hydrate | | logs | no | Forced empty | | profilerEnabled | yes | Editor profiler (line % when engine provides profile) | | inlineDebugEnabled | yes | End-of-line Debug chips from last-run logs | | debugPinsEnabled | yes | Chart pin markers + editor pin gutter | | editorRulerEnabled | yes | -col guide (default on) | | editorWrapEnabled | yes | Soft line wrap (default on; stats strip wrap toggle) | | panelChrome / alertsPanel / layerPanel / dataViewPanel | yes | Dock chrome (side-by-side left/right) | | chartLayout / savedLayouts | yes | Multi-chart grid + named layouts | | compare | prefs yes | Second-symbol overlay (bars ephemeral) | | drawingTool | reset cursor | On hydrate | | drawings / drawingPrefs / drawingUi | yes | User annotations + toolbar prefs | | selectedDrawingId | no | Ephemeral selection | \Status fields may restore but boot also appends fresh logs. Defaults (product) Representative defaults from store: | Key | Default | | --- | --- | | symbol | BTCUSDT | | interval | d | | source / active source | binance-rest | | stream | binance-ws | | engine | server | | storage | local | | endpoint | demo/API host or localhost depending on build era | | editor width | docked open | | watchlist | majors, s refresh | Treat demo endpoints as overridable—operators should set local URLs in desk topology. setActivePlugin ``text setActivePlugin(kind, id): activePlugins[kind] = id if source → source = id if engine → engine = id if stream → live.streamId = id persist() ` Prevents registry/store split brain (architecture overview). Logging `text appendLog(level, message, source?) levels: info | ok | warn | error MAX_LOGS = ` setStatus maps app status to log levels for important transitions. Legacy dual path Vanilla state.js remains for legacy shell with the same storage key family. Product AXIS uses Solid store only—do not dual-write new features to state.js unless maintaining legacy. Invariants . Hydrate never restores lastRun or logs. . Persist strips bars. . Key migration is read-repair (state namespaces). . pluginsConfig keys prefer ${kind}:${id} . . Store is not a plugin registry—ids must exist in registry to function at runtime. Failure modes | Mode | Behavior | | --- | --- | | JSON parse fail | Defaults | | localStorage throws | No-op persist | | Unknown plugin id | UI may show id; run/load fails at getActive | | Oversized drawings | Quota risk—export/clear | Testing notes Import tests/setup.ts` for localStorage stubs Unit coverage includes store + migration patterns Do not require real IDB in pure store tests See also State namespaces ADR- UI shell --- FILE: ../axis/docs/ui/ui-shell.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "UI shell" description: "AXIS chrome: topbar, watchlist, status bar, settings, plugin manager, logs, and layout chrome around the chart." --- UI shell The shell is the non-canvas chrome: navigation of axes, market selection, modals, and status. It binds human gestures to store + plugins without owning calculation. Abstract | Component | Path | Role | | --- | --- | --- | | Topbar | ui/Topbar.tsx | Axes pickers, Load/Run/Live, theme, modals | | Watchlist | ui/Watchlist.tsx | Symbols, quotes, click-to-load | | StatusBar | ui/StatusBar.tsx | Status, engine/storage badges, trade snippet | | SettingsDialog | ui/SettingsDialog.tsx | Endpoint, engine, storage, intervals | | PluginManager | ui/PluginManager.tsx | Catalog / install / library | | ArchitectureModal | ui/ArchitectureModal.tsx | Compose-recipe wiring (source × stream × engine × storage × dataset) | | SystemLogs | ui/SystemLogs.tsx | Ring buffer of store.logs | | ResultsPanel | ui/ResultsPanel.tsx | Bottom drawer (see results doc) | | Icons | ui/icons.tsx | Lucide wrappers | | ResizeHandle | ui/ResizeHandle.tsx | Panel widths/heights | Conceptual model Topbar responsibilities Grouped left→right (axis-tb-group, data-tb-group): | Group | Contents | | --- | --- | | brand | Logo + AXIS | | market | Symbol, Interval, Type (TopbarField), Compare | | data | Source (+ CSV upload), Load, Reload (icon-only) | | compute | Engine, Stream, Run, Live, Replay | | layout | Multi-chart layouts + recipes | | panels | List, Editor, Library, Data (Source Manager), Scripts, Layers, Alerts, Data window, Inputs, Results | | system | Wire (Architecture), Runtimes, Settings, Theme (margin-left: auto) | | Control | Effect | | --- | --- | | Symbol / interval | Store + Load (Enter / blur reload) | | Source / Stream / Engine | setActivePlugin + side effects | | Chart type | setChartType (candles, HA, …) | | Compare | Second-symbol overlay | | Load / Reload | One-shot historical fetch → chart | | Data panel | Data Source Manager — background deep history | | Scripts panel | Applied indicators/strategies list | | Run | Editor doc → runAndApply; accent only while status === 'running' | | Live | multiplex start/stop (stops Replay) | Chart themes Settings / command palette expose ten curated presets (void dark/light, classic, mono, obsidian, graphite, pacific, dusk, porcelain, parchment). High-end soft surfaces — not neon high-contrast. See src/theme/presets.ts. | Replay | Bar replay over loaded bars | | Panel toggles | isPanelOpen / dual-write chrome | | Settings / Plugins | Modal open | | Detach editor | Bridge + popout | Integrated labels: ui/TopbarField.tsx (.axis-tb-field). catalogTick forces memo re-list when plugins install/remove. Command palette Ctrl/Cmd+K — CommandPalette + command-registry.ts: panels, theme, layouts, Run, symbol focus, editor toggles (ruler, debug, pins, profiler), jump to line, git push/pull, save library. Alerts panel Dockable Alerts (src/alerts/ + AlertsPanel.tsx): local-first price alerts, webhook optional, list/create/toggle. Continuous server-side arming is not required for session use. Workspace snapshots storage/workspace-snapshot.ts + optional chrome menus capture layout/plugin prefs for restore (distinct from OHLCV — bars still re-fetch on Load). Panel docks (side-by-side) Left/right docks with + open panels lay out horizontally (row), not stacked under each other: Example: Indicators left of Editor on the right strip Column width = sum of panel widths (capped) Each panel keeps its own width (resize handle on chart-facing edge) Bottom dock still stacks vertically See ui/panels/dock-layout.ts, FloatableShell.tsx. Watchlist Default universe includes majors (BTCUSDT, …) refreshSec clamped – Source-aware labels (e.g. CSV mode) Click sets symbol and loads through active source Status bar Single row: Left — Connection HUD (ConnectionHud, data-testid="axis-connection-hud") LIVE badge (off / connecting / live / err) Tick pulse (fixed-width price + age; ping does not resize layout) Engine chip (id · interpret/compile · latency) Plane chips SRC / STR / ENG / STO with transport badges WS / REST / LOCAL / BROKER Sourcestream pairing warning when mismatched Right — status / statusMessage, strategy snippet, log toggle, bar/ind/pane counts Telemetry is ephemeral (store.telemetry); HUD compact mode and live prefs persist via settings. Live settings (Settings dialog) | Setting | Default | Effect | | --- | --- | --- | | Auto-start live after Load | off | live.preferAfterLoad → multiplex after successful Load | | Indicator re-run | every-tick | or bar-close only | | Compact connection HUD | off | hide plane chips | Logs System Logs strip appendLog(level, message, source) Cap MAXLOGS () Not persisted Boot messages, pyodide ready, live errors Scriptlogs & Profiler Script log. output and editor line-timing live on a separate path — see Debugging. Controls sit on the editor header (not the topbar) and do not write into store.logs. Theming store.theme → document.documentElement data-theme (dark \| light). Void dark is default product aesthetic. Accessibility / test hooks EE selectors use data-testid on critical controls (axis-btn-load, axis-manager, …) per TESTING.md. Invariants . Shell never calls Python. . Plugin lists always come from registry facades (listSources, …). . Modal settings persist only on Save (not on every keystroke of endpoint field until save). . Live off leaves historical bars intact. Failure modes | Symptom | Shell-side check | | --- | --- | | Pickers empty | Builtins not registered at boot | | Live ignores click | Stream none or multiplex error in logs | | Settings probe fails | Network / CORS—not topbar bug | See also Manager and settings Store Architecture overview --- FILE: ../axis/docs/worker/auth.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Worker auth" description: "API keys (pn…), fail-closed D without KV, admin token, Bearer, ALLOWOPENKEYS, and /api/run gating." --- Worker auth Abstract AXIS Worker auth is API-key based, not session cookies. Keys are created by admins (X-Admin-Token), validated via KV when bound, and used as Authorization: Bearer on the script library and (when gated) on /api/run. Core helpers: worker/src/auth.ts. Key CRUD: worker/src/keys.ts. Conceptual model Interface surface AuthContext ``ts { key: string; userId: string; tier: string } ` userId is a truncated SHA- of the key (stable partition id). extractBearer . Authorization: Bearer . Else query ?key= requireApiKey Returns either { ok: true, ctx } or { ok: false, status, code, message }. | Code | HTTP | When | | --- | --- | --- | | NOKEY | | Empty Bearer / ?key= | | INVALIDKEY | | Unknown in KV / malformed shape | | APIKEYSREQUIRED | | Fail-closed: D (DB) bound without APIKEYS KV and ALLOWOPENKEYS off | Fail-closed with durable storage (..+) When D is active but APIKEYS KV is not bound, inventable shape-only keys would partition real script data by attacker-chosen tokens with no mint/revoke path. In that configuration: . Only explicit ALLOWOPENKEYS= accepts any non-empty Bearer (local demos). . Otherwise respond APIKEYSREQUIRED — bind APIKEYS and mint via /api/keys. /api/run auth gate From worker/src/runtime.ts: | Condition | Auth on POST /api/run | | --- | --- | | APIKEYS bound | Required | | REQUIRERUNAUTH= (or true / yes) | Required | | Neither | Optional (Bearer still meters when present) | Always rate-limited (/min) regardless of auth — see Runtime. Admin keys API (/api/keys) | Action | Auth | Behavior | | --- | --- | --- | | create (POST or ?action=create) | X-Admin-Token === env.ADMINTOKEN | Mint pn + hex, store key:{key} in KV (y TTL) with tier | | validate (GET or ?action=validate) | Bearer or ?key= | Tier + createdat if known | Tiers: free \| hobby \| pro \| team \| enterprise. Without ADMINTOKEN set, create always fails (isAdmin false). Without KV on validate: accepts well-formed pn[a-f-]{} as hobby (dev). ALLOWOPENKEYS wrangler.toml defaults local demos to "": accept any non-empty Bearer key for /api/scripts Never leave open in production. Prefer bound APIKEYS + real admin-minted keys. Internals | Path | Role | | --- | --- | | worker/src/auth.ts | Bearer, hash, requireApiKey (fail-closed) | | worker/src/keys.ts | Admin create / validate | | worker/src/runtime.ts | Run gate + rate limit + usage meter | | worker/src/scripts.ts | requireApiKey on library CRUD | | worker/src/git-oauth.ts | Env GITHUBOAUTHCLIENTID / GITLABOAUTHCLIENTID wins over body clientId | Key format `ts 'pn' + random bytes as hex // hex chars ` Invariants & edge cases . Admin token is a shared secret — rotate via wrangler secrets in production, not committed vars. . KV record shape — JSON { key, tier, createdAt } under key:${apiKey}. . No OAuth / cookies — PWA stores the key in plugin config (storage:cloud). . Stream DO unauthenticated — public market data only. Worked examples Create a key (admin) `bash curl -sS -X POST 'http://...:/api/keys?action=create' \ -H "X-Admin-Token: $ADMINTOKEN" \ -H 'content-type: application/json' \ -d '{"tier":"hobby"}' ` Validate `bash curl -sS 'http://...:/api/keys?action=validate' \ -H "Authorization: Bearer pn…" ` Failure modes | Symptom | Cause | | --- | --- | | create | Wrong/missing admin token | | scripts | Open keys off + no KV + bad shape | | APIKEYSREQUIRED | D without APIKEYS KV and open keys off — bind KV + mint keys | | on /api/run | APIKEYS or REQUIRERUNAUTH set; missing Bearer | | OAuth start uses attacker client | Env client id not set — set GITHUBOAUTHCLIENTID (env always wins over body) | | Cross-user leakage tests fail | Partition hash regression | See also Data plane Bindings Runtime Security tests AXIS CLI — axis secret put · axis setup oauth` --- FILE: ../axis/docs/worker/bindings.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Worker bindings" description: "wrangler.toml vars, KV, D, R, Durable Objects — provision once, paste IDs, frozen project name." --- Worker bindings Abstract Bindings and vars for the AXIS Worker are declared in worker/wrangler.toml (copy from wrangler.toml.example via axis setup worker). Many production bindings ship commented so clone-and-dev does not require Cloudflare resources; wrangler dev synthesizes local substitutes where possible. Project name is frozen: name = "pynescript-axis". Conceptual model Interface surface — Env From src/index.ts: ``ts interface Env { APIKEYS?: KVNamespace; USAGE?: KVNamespace; DB?: DDatabase; BUNDLES?: RBucket; SESSIONS?: DurableObjectNamespace; EXTERNALBACKEND?: string; ALLOWEDORIGIN?: string; ADMINTOKEN?: string; PYODIDEINWORKER?: string; ALLOWOPENKEYS?: string; REQUIRERUNAUTH?: string; GITHUBOAUTHCLIENTID?: string; GITLABOAUTHCLIENTID?: string; } ` Vars ([vars]) | Var | Default (repo) | Role | | --- | --- | --- | | EXTERNALBACKEND | "" | Flask (or other) base URL for /api/run proxy | | ALLOWEDORIGIN | https://pynescript.ai | Extra CORS allowlist (comma-separated); product hosts built-in | | ADMINTOKEN | "" | Shared secret for key minting | | PYODIDEINWORKER | "disabled" | Gate in-worker Python | | ALLOWOPENKEYS | "" | Local demos; set "" in prod | | REQUIRERUNAUTH | unset | "" forces Bearer on /api/run | | GITHUBOAUTHCLIENTID | unset | Preferred OAuth App id (wins over body) | | GITLABOAUTHCLIENTID | unset | Same for GitLab | Prefer secrets for ADMINTOKEN and production backends: `bash CLI-first axis secret put ADMINTOKEN axis secret put EXTERNALBACKEND or: wrangler secret put … ` Prod invariant: if DB (D) is bound, also bind APIKEYS KV — otherwise script routes fail closed with APIKEYSREQUIRED. Provision recipes KV `bash wrangler kv namespace create APIKEYS wrangler kv namespace create USAGE paste ids into wrangler.toml [[kvnamespaces]] ` D `bash wrangler d create pynescript [[ddatabases]] binding = "DB" (name MUST be DB) wrangler d execute pynescript --remote --file=schemas/scripts.sql ` Repo may already contain a databaseid for the project’s D — treat IDs as environment-specific when forking. R (optional) `bash wrangler r bucket create indicator-bundles binding BUNDLES ` Durable Objects Uncomment bindings + migrations in wrangler.toml, then: `bash wrangler deploy ` Class: SessionDO exported from src/index.ts. Other wrangler settings | Setting | Value | | --- | --- | | main | src/index.ts | | compatibilitydate | -- | | compatibilityflags | nodejscompat | | [observability] | enabled = true | Static PWA assets are not served by this Worker in the documented split: Pages serves dist/, Worker serves API/WS. Project name for Pages deploy remains pynescript-axis. Deploy commands `bash cd frontend/worker wrangler deploy Pages (from frontend/) bun run build wrangler pages deploy dist --project-name=pynescript-axis or npm run deploy:pages from worker package ` Makefile: make worker-deploy, make pages-deploy (note: some Makefile targets still point at legacy tree — prefer dist/ from Vite build; see build and serve). Invariants & edge cases . Do not rename CF project without migrating all binding IDs, custom domains, and CI. . Local wrangler auto-provisions mock KV/D; production needs real IDs. . EXTERNALBACKEND=localhost on CF cannot reach your laptop — use a public tunnel or co-located VPS. . Health reports which optional bindings are present. Failure modes | Deploy / runtime issue | Fix | | --- | --- | | Deploy fails placeholder KV | Uncomment only after create, or leave commented | | Scripts empty | Apply SQL schema | | Stream | Enable DO binding + migration | | CORS fails prod origin | Set ALLOWED_ORIGIN` to exact Pages origin | See also Cloudflare DevOps Runtime CORS --- FILE: ../axis/docs/worker/data-plane.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Worker data plane" description: "Script library API: /api/scripts, D schema, drafts, revisions, and in-memory fallback." --- Worker data plane Abstract The Worker data plane for user scripts is frontend/worker/src/scripts.ts — REST under /api/scripts with Bearer auth (auth). Persistence prefers D (env.DB); without D, per-isolate in-memory maps support local wrangler dev. The PWA cloud storage plugin is the primary client. Conceptual model Interface surface All routes require valid API key unless noted. | Path | Methods | Behavior | | --- | --- | --- | | /api/scripts | GET | List meta for userId | | /api/scripts | POST | Create script | | /api/scripts/:id | GET | Read full document | | /api/scripts/:id | PUT | Upsert; honors If-Match revision | | /api/scripts/:id | DELETE | Remove | | /api/scripts/draft | GET/PUT | Per-user draft buffer | Document fields | Field | Notes | | --- | --- | | id | Client or server assigned | | name, description, path | Meta | | content | Pine source | | revision | Opaque string (rev…) for concurrency | | createdat / updatedat | ms epochs (API may camelCase in JSON) | Concurrency PUT with If-Match: rejects mismatched revisions ( path covered in security tests). Fresh writes mint newRevision(). Internals D schema (schemas/scripts.sql) ``sql CREATE TABLE scripts ( userid TEXT NOT NULL, id TEXT NOT NULL, name TEXT NOT NULL, description TEXT, path TEXT, content TEXT NOT NULL, revision TEXT NOT NULL, createdat INTEGER NOT NULL, updatedat INTEGER NOT NULL, PRIMARY KEY (userid, id) ); CREATE TABLE scriptdrafts ( userid TEXT PRIMARY KEY, content TEXT NOT NULL, name TEXT, updatedat INTEGER NOT NULL ); ` Apply: `bash wrangler d execute pynescript --remote --file=schemas/scripts.sql wrangler d execute pynescript --local --file=schemas/scripts.sql ` Binding name in wrangler.toml must be DB (code uses env.DB). Database name can be pynescript. Partitioning userId = SHA-(api_key).hex.slice(, ) — raw keys are not stored as D partition ids. Health feature flags `json { "status": "healthy", "service": "pynescript-axis-worker", "features": { "scripts": true, "d": true/false, "keys": true/false } } ` Optional R BUNDLES R is reserved for indicator / pynescript wheels (RUNTIME.md). Not required for scripts CRUD. Invariants & edge cases . Memory store is not multi-isolate durable — production needs D. . Drafts ≠ commits — drafts are crash buffers; library list is separate. . CORS — scripts handler sets allow headers including If-Match`. . Cloud plugin dual-write — browser still saves drafts to local storage. Failure modes | Code / symptom | Meaning | | --- | --- | | | Missing/invalid Bearer | | | Unknown script id for user | | | Revision conflict | | Empty list after deploy | Schema not applied / wrong DB id | See also Storage plugins Auth Bindings --- FILE: ../axis/docs/worker/durable-objects.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Durable Objects" description: "SessionDO WebSocket relay: one Binance upstream per session, fan-out to browser clients." --- Durable Objects Abstract SessionDO (frontend/worker/src/durable-objects/session.ts) is a per-session WebSocket relay. Browsers connect to the Worker at /api/stream; the Worker pins a Durable Object by session name and the DO opens one upstream Binance kline socket, broadcasting payloads to connected clients. Goal: survive browser per-origin WebSocket connection limits and share upstream subscriptions. Conceptual model Interface surface Client → Worker `` wss:///api/stream?session=&symbol=BTCUSDT&interval=m ` frontend/worker/src/index.ts: Requires env.SESSIONS binding; else NODO. idFromName(session || 'default'). Rewrites request to DO path /ws?… and returns upgrade response. DO HTTP surface | Path | Role | | --- | --- | | /ws | Only path; requires Upgrade: websocket (else ) | Query: symbol (default BTCUSDT), interval (default m). Client → DO messages (JSON) | Message | Effect | | --- | --- | | { "action": "subscribe", "symbol", "interval" } | Rebind upstream if changed | | { "action": "ping" } | { action: "pong", t } | DO → client Raw Binance kline JSON (same as direct venue WS). Status: { type: 'status', state: 'open'|'closed', url? } Errors: { type: 'error', message } Example plugin frontend/public/plugins/example-cf-do-stream.js maps kline fields to Bar and builds the Worker URL from config endpoint. Internals | Topic | Detail | | --- | --- | | Upstream URL | wss://stream.binance.com:/ws/{symbol}@kline{interval} | | Client lifecycle | acceptWebSocket; on last client close → closeUpstream | | Hibernation | Comment intent: hibernate when empty; implementation uses explicit close | | Fan-out | broadcast to all clients | wrangler binding (often commented until ready) `toml [[durableobjects.bindings]] name = "SESSIONS" classname = "SessionDO" [[migrations]] tag = "v" newsqliteclasses = ["SessionDO"] ` SessionDO is re-exported from src/index.ts for the Workers runtime class registry. Invariants & edge cases . Session key — example plugin uses symbol@interval as session name so like-minded tabs share one DO. . No auth on stream today — treat as public relay of public market data; do not put secrets in query strings. . Upstream hard-coded Binance — not a generic multiprovider DO yet. . Binding optional in local — without SESSIONS, stream API fails closed with NODO. Failure modes | Symptom | Cause | | --- | --- | | NO_DO | Binding commented / not deployed | | expected websocket | Non-upgrade GET | | No bars | Upstream block or wrong interval string | | Stale symbol | Client did not send subscribe` after change | See also Streams Bindings Plugin examples --- FILE: ../axis/docs/worker/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Worker" description: "Cloudflare Worker for AXIS: /api/run proxy, keys, scripts, usage, and Durable Object stream relay." --- Worker Abstract The AXIS Cloudflare Worker (worker/) is the edge data plane for the PWA: JSON APIs, optional script library (D), API keys (KV), usage meters, on-chain proxy, and a WebSocket session relay (Durable Objects). It is not a full Pine Script interpreter in production today. Honest runtime fact: POST /api/run proxies to EXTERNALBACKEND (typically Flask) unless PYODIDEINWORKER=enabled and the in-worker path succeeds. In-worker Pyodide is planned / feature-gated — see Runtime and worker/RUNTIME.md. Security (..+): gated /api/run (auth when APIKEYS / REQUIRERUNAUTH), rate limits + body caps, fail-closed Worker auth when D is bound without APIKEYS KV, product-scoped CORS (not open .pages.dev), OAuth env client ids over body clientId. See Auth and CORS. Operator path: AXIS CLI — axis setup · axis deploy worker · axis health. Conceptual model Infrastructure names (frozen) | Surface | Value | | --- | --- | | Wrangler / Pages project | pynescript-axis — do not rename lightly | | npm package | axis-worker | | Health JSON service | pynescript-axis-worker | | Product brand | AXIS | Custom domains / aliases may say axis.; leave the CF project id stable so KV/D IDs and CI stay valid. Interface surface | Path | Method | Role | | --- | --- | --- | | /, /health | GET | Health + feature flags (scripts, d, keys, onchain) | | /api/run | POST | Run script (proxy / gated Pyodide) | | /api/keys | GET/POST | Validate / create keys (X-Admin-Token for create) | | /api/usage | GET | Usage stub / KV-backed counters | | /api/scripts… | CRUD | Script library (Bearer) | | /api/onchain/… | GET | Public on-chain proxy (DefiLlama + GeckoTerminal allowlist; see below) | | /api/stream | WS | Session DO relay | On-chain proxy routes (worker/src/onchain.ts) Allowlisted GET only (public, no API key). Other /api/onchain/ paths return . | Worker path | Upstream | Cache TTL | | --- | --- | --- | | /api/onchain/health | local (providers + cache size) | — | | /api/onchain/llama/protocols | https://api.llama.fi/protocols | ~ min | | /api/onchain/llama/protocol/:slug | https://api.llama.fi/protocol/:slug | ~ min | | /api/onchain/gecko/networks/:network/pools/:address/ohlcv/:timeframe | https://api.geckoterminal.com/api/v/networks/.../ohlcv/... | ~ s | | /api/onchain/gecko/search/pools | https://api.geckoterminal.com/api/v/search/pools | ~ s | Gecko validation: network ^[a-z-]+$; address EVM x+ hex or Solana-style base –; timeframe day \| hour \| minute. OHLCV query pass-through: aggregate, limit, currency, beforetimestamp. Search: query, network, page, include. Responses may include X-Axis-Onchain-Cache: HIT|MISS. Product guide: On-Chain data. Entry: worker/src/index.ts. Local entrypoints ``bash cd frontend/worker npm install or bun install npm run dev wrangler dev → http://...: ` Makefile: make worker-dev, make worker-deploy. Implementation status | Feature | Status | | --- | --- | | /api/run → EXTERNALBACKEND | Works today | | Admin /api/keys | Works (KV or shape-check) | | Usage KV increment on run | Works when USAGE bound | | SessionDO + /api/stream | Implemented; binding often commented until provisioned | | /api/scripts + D schema | Implemented; memory fallback without D | | Pyodide scaffold | pyodideruntime.ts + flag; wheel pipeline incomplete | | Response cache for /api/run | Deferred | Page map | Page | Topic | | --- | --- | | Runtime | /api/run, proxy, Pyodide plan | | Durable Objects | Stream fan-out | | Data plane | Scripts, D, drafts | | Auth | Bearer keys, admin token | | Bindings | wrangler.toml, vars, provision | See also Cloudflare deploy Engines Worker README: frontend/worker/README.md` --- FILE: ../axis/docs/worker/runtime.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-only --- title: "Worker runtime" description: "POST /api/run — auth gate, rate limits, size caps, EXTERNALBACKEND proxy, feature-gated in-worker Pyodide." --- Worker runtime Abstract worker/src/runtime.ts implements handleRun for POST /api/run. Execution preference is documented in-file: . PYODIDEINWORKER=enabled → tryRunInWorker (pyodideruntime.ts) . Else EXTERNALBACKEND set → HTTP proxy to ${EXTERNALBACKEND}/run . Else NOBACKEND with a pointer at env vars and worker/RUNTIME.md Production reality: ships as a proxy to Flask (or another Python host). In-worker Pyodide is a scaffold, not a complete wheel-upload pipeline. Auth & abuse controls (..+) | Control | Behavior | | --- | --- | | Auth gate | When APIKEYS KV is bound or REQUIRERUNAUTH=, requireApiKey is mandatory | | Rate limit | requests / minute per IP (and per key when authenticated) | | Script cap | Max KiB source chars | | Bars cap | Max OHLCV rows per run | | Upstream timeout | s proxy abort to EXTERNALBACKEND | Optional Authorization: Bearer still increments USAGE KV when bound (usage:, -day TTL). Conceptual model Interface surface Request body ``ts { script: string; // required non-empty data: Bar[]; // required non-empty OHLCV array mode?: 'interpret' | 'compile'; } ` Invalid bodies → { status: 'error', code: 'BADREQUEST', message }. Success / upstream Proxy returns upstream status and body verbatim (JSON content-type, CORS origin header). Flask Pro API is the usual upstream. Error codes (Worker-originated) | Code | HTTP | Meaning | | --- | --- | --- | | BADREQUEST | | Validation / size caps | | NOKEY / INVALIDKEY | | Auth required and missing/unknown key | | RATELIMIT | | > runs/min for IP or key | | NOBACKEND | | No external backend and Pyodide path off/failed path to proxy | | (Pyodide) | body error | tryRunInWorker may return { status: 'error', error } JSON | Usage metering If Authorization: Bearer … and USAGE KV bound: ` usage:{key} → count++, TTL days ` Internals | Path | Role | | --- | --- | | worker/src/runtime.ts | auth gate, rate limit, validate, meter, dispatch | | worker/src/pyodideruntime.ts | gated boot + runscript stub path | | worker/RUNTIME.md | architecture plan for wheels / R / cold start | | worker/wrangler.toml [vars] | EXTERNALBACKEND, PYODIDEINWORKER, REQUIRERUNAUTH | pyodideruntime scaffold When enabled: Lazy-loads Pyodide from jsDelivr v.. (dev fallback). Production intent: R wheels + module-level cache (RUNTIME.md). Calls runscript(script, bars) via runPythonAsync — requires runtime bootstrap that is not fully wired like the browser engine’s pynescriptruntime.py + micropip install. Do not enable in production without completing the wheel pipeline and load testing CPU/memory limits. Constraints (from RUNTIME.md) | Resource | Budget | | --- | --- | | CPU (paid) | s limit; typical bars ≪ s target | | Memory | MB; Pyodide ~ MB + wheel ~ MB | | Cold start | ~s first Pyodide boot (plan) | Roll-out flag RUNTIME.md also mentions RUNTIMEMODE=in-worker as product language; code checks PYODIDEINWORKER === 'enabled'. Prefer the code flag when configuring. Invariants & edge cases . Body stream — request body is consumed once; proxy re-serializes the already-parsed object. . CORS — run responses set Access-Control-Allow-Origin from pickOrigin (see CORS). . PWA path mismatch — browser server engine posts to {endpoint}/run; Worker route is /api/run. Align via gateway rewrite or endpoint base path convention. . No response caching yet — README lists cache for identical (script, datahash) as deferred. Worked examples Local proxy to Flask `toml wrangler.toml [vars] EXTERNALBACKEND = "http://...:" PYODIDEINWORKER = "disabled" ` `bash curl -sS -X POST http://...:/api/run \ -H 'content-type: application/json' \ -d '{"script":"//@version=\nindicator(\"t\")\nplot(close)","data":[{"time":,"open":,"high":,"low":,"close":}]}' ` Force for missing backend Unset EXTERNALBACKEND, keep Pyodide disabled → expect NOBACKEND message referencing RUNTIME.md. Failure modes | Symptom | Fix | | --- | --- | | NOBACKEND | Set EXTERNALBACKEND or finish Pyodide path | | Proxy /connection refused | Flask not listening; wrong URL from Worker (use tunnel/public URL in CF, not localhost) | | Pyodide enabled but errors | Wheel missing; CDN blocked; incomplete runscript bootstrap | See also Bindings Engines Auth (Bearer when gated; metering) CORS and origins AXIS CLI — axis deploy worker · axis health` ---