Go to file
dev 34c0789f2c Remove the working screenshots and name the page after the service
Three things found in the check before a release. A directory of
screenshots taken while working on the interface had been committed; it
is removed and ignored, because it is a working note rather than part of
the service. Two branches kept pointing at the history from before the
licence changed, which is the only place that history was still reachable;
they are gone. The page title still named the service the way it was named
before it became apsis-book.

The binary was checked from an empty directory with no content tree and no
frontend build beside it: it serves every route from what it carries.
2026-10-02 13:05:37 +08:00
content Take the interface formulas from the library, now written as math 2026-10-02 12:56:00 +08:00
docs Write the notation in the text, not in a code span 2026-10-02 12:45:56 +08:00
frontend Remove the working screenshots and name the page after the service 2026-10-02 13:05:37 +08:00
scripts Take the interface translation from the apsis-rs doc comments 2026-10-02 09:47:02 +08:00
src Write the notation in the text, not in a code span 2026-10-02 12:45:56 +08:00
tests Write the notation in the text, not in a code span 2026-10-02 12:45:56 +08:00
.gitignore Remove the working screenshots and name the page after the service 2026-10-02 13:05:37 +08:00
Cargo.lock Rename the service to apsis-book 2026-10-02 01:24:42 +08:00
Cargo.toml Move every test out of src, as apsis-rs does 2026-10-02 01:56:21 +08:00
LICENSE web: add the apsis-web service, content, and frontend 2026-09-27 00:53:20 +08:00
README.md Write the formulas in the notation Markdown has for them 2026-10-02 11:52:17 +08:00
README.zh.md Write the formulas in the notation Markdown has for them 2026-10-02 11:52:17 +08:00
build.rs web: add the apsis-web service, content, and frontend 2026-09-27 00:53:20 +08:00
rust-toolchain.toml web: add the apsis-web service, content, and frontend 2026-09-27 00:53:20 +08:00

README.md

apsis-book

简体中文 | English

The web service layer of ApsisRS. It presents the ApsisRS computational kernels for commercial spaceflight and satellite internet systems in a browser: the concept documentation, a knowledge graph, the interface documentation extracted from the library source, and an online tool set that runs the kernels on parameters the user supplies.

The service builds to a single binary that embeds every asset it serves. It does not read a content directory or a frontend build at run time, and it does not need either to be present next to the binary.

What it serves

Section Content Route prefix
Concepts Documents on the time scales, the reference frames, the element formats, and the propagation models, with the units and the applicable range of each /api/docs
Knowledge graph The concepts and their relationships, plus the crate structure of the library and the algorithm flow graphs /api/graph
Interface The public interface of the ApsisRS layers the service builds against, extracted from the source doc comments /api/api
Online tools TLE parsing, SGP4 propagation, track sampling, time scale conversion, coordinate conversion, frame rotation, Keplerian conversion, and Hohmann transfer planning /api/tools

Design constraints

The service follows the constraints of the workspace it belongs to.

  • One runtime dependency. The only entry in [dependencies] is apsis-rs, through a path dependency. The HTTP server, the JSON codec, the router, the Markdown renderer, and the graph model are all written against the standard library.
  • No unsafe. The crate does not contain an unsafe block.
  • No panic in a request path. A malformed request, a bad parameter, or a failing computation produces a response, not a panic. The worker that serves a request catches a panic in the handler as a final guard and answers with a server error.
  • Self-contained comments. Every comment explains the code, the algorithm, the interface, or the project. No comment refers to a private document, an instruction, or a plan.

Building

The frontend must be built before the release binary so that its output can be embedded. A release build does this automatically.

Every built asset carries a content hash in its name, so replacing the binary replaces those names. A browser therefore cannot be holding a stale bundle: the new bundle is requested under a URL it has never seen. The entry point is served with no-cache, must-revalidate, and hashed assets with public, max-age=31536000, immutable. The service registers no service worker.

# Development: a debug build embeds whatever is in frontend/dist and does
# not run the frontend build.
cargo build

# Release: build.rs builds the frontend and embeds the result.
cargo build --release

The binary is at target/release/apsis-book.

Environment variables:

Variable Effect
APSIS_BOOK_HOST Interface to bind, default 127.0.0.1
APSIS_BOOK_PORT Port to bind, default 8080
APSIS_BOOK_ADDRESS Bind address as HOST:PORT, overriding the two above
APSIS_BOOK_WORKERS Worker threads, default 4
APSIS_BOOK_SKIP_FRONTEND Set to 1 to skip the frontend build in a release build
APSIS_BOOK_FRONTEND_DIR Frontend directory, default frontend

Command line options override the environment:

# Bind every interface on port 8080.
apsis-book --host 0.0.0.0 --port 8080

# The combined form is equivalent.
apsis-book --address 0.0.0.0:8080

--host and --port may be given together. --address cannot be combined with either of them, because the order in which they would apply is not defined. A host may be a literal address, an IPv6 literal in brackets, or a name the operating system resolves.

Language

The interface is bilingual and opens in Chinese. The reader switches to English with the control in the top bar; the choice is stored in the browser and applies to every page.

The backend carries every user facing string in both languages, so the switch takes effect without a request. A concept document is written once in English as content/concepts/<id>.md and translated as content/concepts/<id>.zh.md. A document without a translation is shown in English to every reader, with a note saying so.

Running

./target/release/apsis-book --address 127.0.0.1:8080

Open http://127.0.0.1:8080. The JSON interfaces are available without the frontend:

  • GET /health
  • GET /api/docs, GET /api/docs/<id>, GET /api/docs/search?q=
  • GET /api/graph, GET /api/graph/crates, GET /api/graph/flows, GET /api/graph/<flow>
  • GET /api/api, GET /api/api/crates/<name>, GET /api/api/items/<name>
  • GET /api/tools, GET /api/tools/<id>, POST /api/tools/<id>/run

A tool call takes a JSON object of parameters and returns a shaped result:

curl -s http://127.0.0.1:8080/api/tools/hohmann-transfer/run \
  -H 'content-type: application/json' \
  -d '{"from_radius": 6778, "to_radius": 42164}'

Content layout

The content is a superset of what any one library implements. It has two groups.

概念 / Concepts describes the field. The layers follow the ones the library is built in, because that is the order a reader meets the subject:

Layer Subject
导论 Introduction Scope, organisation, and how to read a page
基础层 Foundations Units, dimensions, numerical method, error
物理层 Physics Time scales and reference frames, which are two unrelated subjects and are both held here
轨道层 Orbit Elements, element formats, perturbations, propagators, manoeuvres
射频与传播 Radio and propagation Doppler, link budgets, the medium, interference
星座与网络 Constellation and network Constellations, access, topology, handover, routing
运行层 Operations Telemetry, tracking, command, planning
仿真层 Simulation Scenarios, events, and what a result means

实现 / Implementation describes what one particular library implements of the above, including how it is organised and where its algorithms come from. Keeping it separate is what makes the concept set independent: a page about link budgets does not change when a library changes.

A document belongs to the layer named by the directory it is in, so adding a document means creating it under its layer and listing it in chapters.json.

Regenerating the interface catalog

The interface documentation is generated from the ApsisRS sources rather than written by hand. The doc comments there are written in English and then in Chinese, and the extractor splits the two halves, so both languages come from the comment beside the code and neither can fall behind what it describes. Run the extractor from this directory when the sources change, then rebuild:

scripts/extract_api.sh
cargo build --release

The script reads the workspace at ../apsis-rs by default; set APSIS_RS_WORKSPACE to point at a checkout elsewhere. It requires Python 3.8 or later. The Rust build does not run it and does not depend on Python.

Editing the content

Path Contents
content/concepts/chapters.json The groups, their layers, and the reading order, with bilingual titles
content/concepts/<layer>/*.md Documents, one directory per layer; the directory gives the layer, the file name gives the identifier
content/concepts/<layer>/*.zh.md Chinese translations, named for the file they translate
content/graph/concepts.json The concept graph
content/flows/*.json The algorithm flow graphs
content/api.json The generated interface catalog, in both languages; do not edit by hand

A formula is written between dollar signs, as $$...$$ for one on a line of its own and $...$ for one within a sentence. That is the notation every Markdown mathematics renderer reads, and it is what the documents use: a formula is mathematics, and a fenced code block is not the place for it. The service translates the notation to MathML, which the browser lays out itself.

A document header declares title, summary, tags, and prerequisites. The identifier and the layer are not declared: the identifier is the file name and the layer is the directory, so the two cannot disagree with the tree. A cross reference is written [[id]] or [[id|label]] and renders as a link that the frontend routes without a page load.

Layout

apsis-book/
├── build.rs                  embed the frontend build and the text content
├── Cargo.toml                one runtime dependency: apsis-rs
├── content/                  authored documents, graphs, and generated catalog
│   └── concepts/<layer>/     documents, one directory per layer
├── frontend/                 Svelte and TypeScript, built by Vite
│   ├── src/lib/              API client, router, components, formatting
│   ├── src/pages/            one component per route
│   └── dist/                 build output, embedded by build.rs
├── scripts/extract_api.sh    interface catalog generator
├── src/
│   ├── api/                  the router and the JSON payloads
│   ├── docs/                 content, Markdown, documents, interface catalog
│   ├── embedded/             access to the embedded frontend assets
│   ├── graph/                the knowledge graph and the flow graphs
│   ├── http/                 request, response, and server
│   ├── tools/                the tool registry, the handlers, the result shape
│   ├── json.rs               the JSON codec
│   ├── lib.rs               the modules, as a library
│   ├── testkit.rs           the few internals the tests reach
│   ├── main.rs               the process entry point
│   └── state.rs              the parsed content, shared by every worker
└── tests/                   every test; `src/` holds none
    ├── service.rs            end to end tests against the built binary
    └── <module>.rs           the module tests

Testing

cargo test          # unit tests and end to end tests
cargo clippy --all-targets
cargo fmt --check
cd frontend && npm run check

The end to end tests start the built binary on an ephemeral port and make plain TCP requests against it.

License

GNU General Public License, version 3 or later, and that alone. See LICENSE.

The service depends on apsis-rs, which is offered under MIT or Apache-2.0. Both of those permit redistribution inside a GPL work, so the two are distributed together under these terms. No other licence is offered for this service.