Skip to content

docs(cloud): restructure the Cloud tab and add verified pricing pages - #33

Merged
Mandalorian-Wang merged 3 commits into
boxlite-ai:mainfrom
Mandalorian-Wang:main
Aug 27, 2026
Merged

docs(cloud): restructure the Cloud tab and add verified pricing pages#33
Mandalorian-Wang merged 3 commits into
boxlite-ai:mainfrom
Mandalorian-Wang:main

Conversation

@Mandalorian-Wang

@Mandalorian-Wang Mandalorian-Wang commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

What this does

Two things: restructures the BoxLite Cloud tab so every navigation root is a summary plus an index, and adds the pricing documentation that was missing entirely.

Cloud goes from 12 pages to 24, still two levels deep.

Navigation roots are now summaries, not monoliths

Root Before After
cloud/index 115 45
cloud/boxes 299 41
cloud/volumes 531 39
cloud/network 461 44
cloud/pricing 281 45

Each root now carries a value proposition, the single warning most likely to bite, an In this section card group, and a "Which page do I want?" intent table. Everything else moved to task-shaped siblings.

The splits were scoped by measuring section sizes, not page length:

network:  one section was 410/462 lines (89%)
volumes:  two sections were 357/532
pricing:  one section was 177/282
boxes:    63 / 35 / 83 / 67  — no dominant section

cloud/boxes was therefore split by topic rather than to relieve bloat, and only after checking that its six inbound anchor links could be repointed.

New structure:

Getting started  index      → vs-opensource, quickstart, api-keys
Boxes            boxes      → images, sizes, lifecycle, from-code
Volumes          volumes    → mount-a-volume, volume-operations, data-across-boxes, reference
Network          network    → tunnels, serve-http, port-forwarding, raw-streams, network-policy
Pricing          pricing    → box-costs, cost-controls, plans, billing

No route that had shipped was deleted or renamed, so no redirects are needed. Anchors that moved were repointed across 10 files.

Pricing documentation

There was none. The old billing page listed plan tiers but never stated a single unit price — the only price signal was "the New Box dialog reports PRICE PER HOUR", which outsources the most-asked question to the product itself.

Rates come from Commerce's anonymous /usage-prices, not from the repo's test fixtures (which happen to carry the same values — a trap worth naming):

Resource Rate
vCPU $0.0504 / vCPU-hour
Memory $0.0144 / GiB-hour
Disk $0.00018 / GiB-hour

New pages:

  • cloud/box-costs — the formula, per-hour/per-month costs for every preset, and which hours are counted: compute billing stops when a stop is requested, disk billing stops when deletion is requested, and there is no minimum duration or hour-rounding.
  • cloud/cost-controlsauto_stop / auto_delete, in Python, Node, and Go. The default configuration stops paying for compute automatically and pays for disk forever; this page closes that loop.
  • cloud/plans — plan tiers plus the break-even points ($20 → Starter, $161 → Pro, $601 → Max) and the constant saving past quota ($11 / $101 / $401 per month).
  • cloud/billing — rewritten as account operations. Adds automatic reload, which the old page never mentioned, and explains why the "available" balance drifts downward with no payment taken (it is the ongoing balance).

The headline fact the old page omitted: a stopped box is still billed for its disk until it is deleted. BOX_STATES_BILLING_DISK_ONLY = [STOPPING, STOPPED].

Correctness fix

cloud/boxes claimed that a box created over REST gets auto_stop=0 and "keeps running until you stop it, and paying for it is your responsibility."

It does not. The SDK omits the field when unset (#[serde(skip_serializing_if = "Option::is_none")]), and the platform resolves the absence to DEFAULT_AUTO_STOP_SECONDS = 900:

// apps/api/src/box/services/box.service.ts:1453
const autoStop = input.autoStop ?? DEFAULT_AUTO_STOP_SECONDS

The unwrap_or(0) that the earlier claim rested on is client-side validation, and its own comment says so: "validate only the caller's requested policy — not the server's auto_stop".

This mattered in the dangerous direction: the page told readers a long unattended job would keep running, while the same page warned elsewhere that such a job is stopped at 15 minutes. Corrected in three places, found by grepping for the class rather than the instance.

Volume CRUD

cloud/volume-operations is now an actual CRUD page. Before this change:

  • rt.volumes.get() had no runnable code anywhere in the docs
  • list() appeared only incidentally, inside a deletion demo
  • the headings were What the SDK does not tell you and Deletion is asynchronous — a reader looking for "how do I list my volumes" would never find them

It now runs create → list → get → delete in one script, in Python, Node, and REST, and documents that there is no update operation: create / list / get / remove is the entire surface, verified by grepping for update|rename|patch|resize|set_name across the Rust core, the Python bindings, and the Node contracts.

Also

  • Removed a dev-environment hostname that had been used to show the rate list was checkable. It leaked internal naming and pointed readers at a non-production host.
  • Deleted cloud/index's runnable script, which duplicated cloud/quickstart and cloud/vs-opensource.
  • Deleted cloud/network's outbound section, which duplicated the fuller one on cloud/network-policy.
  • Renamed preview-urlsserve-http: "preview URL" is a distinct platform concept that network-policy owns, and network-policy says so explicitly.
  • llms.txt is now generated from docs.json and page frontmatter, with --check wired into CI. It was hand-maintained and had drifted to five dead links and no Cloud section.

Verification

Check Result
mint broken-links no broken links found
scripts/lint-docs.py 180 pages, no soft promises
Internal links and anchors 101 pages, all resolve
gen-llms-txt.py --check up to date
All 24 Cloud routes 200 locally
Go example go vet clean against the SDK
Volume API claims re-checked against origin/main after confirming the checkout was current

Not verified

The Starter / Pro / Max price, quota, and concurrency figures on cloud/plans. Commerce's /plan catalog requires authentication (401; a nonexistent route returns 404, so the distinction is real). Those three rows carry over from the previously published page, and the break-even table is derived from them. Worth a glance in the console before merging.

🤖 Generated with Claude Code

I deleted the BoxLite Cloud section from llms.txt in ffe3d36. The sync that
landed that commit deliberately excluded `cloud/` and `docs.json` because both
held content the source tree could not produce — but llms.txt is derived from
the whole page set the same way, and I did not think of it, so the source-tree
version overwrote it and took all 21 Cloud lines with it.

Restoring those lines by hand would leave the underlying problem in place.
llms.txt duplicates two facts that already live elsewhere: the navigation tree
(docs.json) and each page's title and description (its own frontmatter). A
hand-maintained third copy rots silently, and this one had:

* five dead links — manage-sandbox/configuration, guides/production-best-practices,
  guides/building-from-source, guides/macos-sandbox-debugging, and
  architecture/internals, all renamed, moved, or deleted in earlier passes
* group names from a superseded IA ("Manage Sandbox", "Guides", "Resources"
  against the live "Manage sandboxes", "From demo to production",
  "FAQ and releases")
* the three use-case mode groups, flattened in the site long ago
* no BoxLite Cloud section

scripts/gen-llms-txt.py now derives the file from docs.json plus frontmatter.
85 entries, matching the 85 pages in the navigation, with no dead links.

`--check` is wired into the docs-lint workflow so staleness fails CI rather
than waiting for a reader to notice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f84fac16-cfb0-4f8c-9873-051987fd9e15


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The volumes page described an API that no longer exists. It was written
against a checkout 147 commits behind origin/main, and volume support
changed substantially in between.

Verified against boxlite-sdk-main at d354470a, level with origin/main:

* `VolumeInfo` carries a `name`, and a volume mounts by name or by id
  (volumes/store.rs:22-25). The page said `create()` took no arguments
  and that the id was the only handle.
* An entire section, "Names live in the console, ids come from the SDK",
  argued a product difference that no longer exists. The console and the
  SDK now name volumes the same way. Removed.
* The wire field is `managed_volume`; the `volume://` scheme has no
  remaining reference anywhere in the tree.
* Host bind mounts are rejected before any network request goes out
  (rest/runtime.rs:167-177), not silently ignored as the page warned.
  The real error string is now quoted.
* Read-only managed volumes are refused client-side with a specific
  message rather than becoming a server 400.
* `size_bytes` is always empty: neither server response builder returns a
  size field (boxlite-volume.controller.ts:66-81). It was documented as a
  working field.
* Mount paths are validated server-side — absolute, not root, no relative
  components, no consecutive slashes, no system directories. None of this
  was documented; there is now a table.

Structure follows the progressive-disclosure order the style guide
requires, which the previous version did not: Prerequisites, then a
runnable Quick example, then concepts, then the parameter tables. The
first code block a reader met used to be a fragment with undefined
variables, four screens above anything runnable. Four sibling Cloud pages
had a Prerequisites section; this one did not.

Volume state and deletion move to cloud/volume-operations. They serve a
different reader — someone whose volume is already carrying data — and
the deletion poll depends on the `state` field the neighbouring section
explains, so the two travel together. Troubleshooting rows that moved are
not duplicated; the origin page keeps one pointer instead. This also
fills the Volumes nav group, which shipped with an empty `pages` array
and rendered as a chevron that expanded to nothing.

Tab renamed from "BoxLite Opensource" to "BoxLite Open Source".
"Opensource" is not a word, and it was the only place in the tree using
that spelling: the seven other occurrences are all the `/cloud/vs-opensource`
route slug, and prose already used `open source` and `open-source`
correctly throughout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Mandalorian-Wang Mandalorian-Wang changed the title docs: generate llms.txt instead of maintaining it by hand docs: rewrite Cloud volumes against main, generate llms.txt Aug 27, 2026
Every nav group root is now a summary plus an index of its children, and the
substantive content moved into task-shaped sibling pages. Cloud goes from 12
pages to 24, still two levels deep.

    root            before  after
    cloud/index        115     45
    cloud/boxes        299     41
    cloud/volumes      531     39
    cloud/network      461     44
    cloud/pricing      281     45

The splits were scoped by measuring section sizes, not page length. One section
was 89% of cloud/network; two were 67% of cloud/volumes. cloud/boxes had no
dominant section, so it was split by topic rather than to relieve bloat.

New pricing pages, with rates read from Commerce's anonymous /usage-prices
rather than from the repo's test fixtures:

  - cloud/pricing        rates and section index
  - cloud/box-costs      formula, size costs, and which hours are counted
  - cloud/cost-controls  auto_stop/auto_delete, in Python, Node, and Go
  - cloud/plans          plan tiers and the $20/$161/$601 break-even points
  - cloud/billing        rewritten as account operations; adds automatic reload

Correctness fix carried in this change: cloud/boxes claimed a box created over
REST gets auto_stop=0 and "keeps running until you stop it". It does not. The
SDK omits the field when unset (serde skip_serializing_if), and the platform
resolves it to DEFAULT_AUTO_STOP_SECONDS = 900 (box.service.ts resolveLifecycle
Policy). The published page contradicted itself, warning elsewhere that a job is
stopped at 15 minutes. Corrected in three places, found by grepping for the
class rather than the instance.

cloud/volume-operations became an actual CRUD page. rt.volumes.get() had no
runnable code anywhere, list() only appeared incidentally inside a deletion
demo, and the headings named gotchas rather than operations. Also documents that
there is no update operation — create/list/get/remove is the whole surface.

Removed a dev-environment hostname that had been used to show the rate list was
checkable, and deleted cloud/index's runnable script, which duplicated
cloud/quickstart and cloud/vs-opensource.

Verified: mint broken-links clean, lint-docs 180 pages with no soft promises,
101 pages with every internal link and anchor resolving, llms.txt regenerated
and --check clean, all 24 Cloud routes rendering 200 locally, and the Go example
type-checking against the SDK with go vet.

Not verified: the Starter/Pro/Max price, quota, and concurrency figures on
cloud/plans. Commerce's /plan catalog requires authentication, so those three
rows carry over from the previously published page and the break-even table is
derived from them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Mandalorian-Wang Mandalorian-Wang changed the title docs: rewrite Cloud volumes against main, generate llms.txt docs(cloud): restructure the Cloud tab and add verified pricing pages Aug 27, 2026
@Mandalorian-Wang
Mandalorian-Wang merged commit 6cb5ac9 into boxlite-ai:main Aug 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant