159 lines
9.7 KiB
Markdown
159 lines
9.7 KiB
Markdown
# DVX package server
|
|
|
|
The catalogue of add-on packages (`.DPK` files) that DVX's Software program browses, installs, and
|
|
updates from, and where publishers upload them. It is a [calog](../../calog) application over
|
|
SQLite, built on [singemaster](../../singemaster)'s shared modules: accounts, sessions, the log,
|
|
mail, keys, the blob store, and `lib/service.lua`'s route wrapper, account routes, and operator
|
|
checks. The two services can run in one calog process.
|
|
|
|
cp dvx.conf.example dvx.conf # and edit it
|
|
calog dvxserver.lua -- dvx.conf # in this folder
|
|
|
|
The server finds singemaster at `../../singemaster` and calog at `../../calog` from its own folder,
|
|
wherever calog is started; `singe=DIR` and `calog=DIR` arguments say where they are when they are
|
|
elsewhere.
|
|
|
|
## Both services in one process
|
|
|
|
calog ../../singemaster/master.lua dvxserver.lua -- singe=../../singemaster master=master.conf dvx=dvx.conf
|
|
|
|
`calogArgs()` is one list for every script in a run, so each service takes its own configuration
|
|
by name (`master=`, `dvx=`). Each listens on its own `bindPort` with its own database, and the
|
|
shared modules are loaded once per service. `test/run.sh` runs both this way.
|
|
|
|
## How it is deployed
|
|
|
|
As singemaster is: plain HTTP bound to loopback, behind a reverse proxy that answers for the public
|
|
name. One difference matters: DVX's web library speaks plain HTTP only (there is no TLS on a DOS
|
|
machine), so the proxy must answer DVX's requests over plain HTTP as well as HTTPS. That costs no
|
|
trust, because no package is trusted for the way it arrived: DVX installs a package only when the
|
|
repository's signature of it checks against the repository key DVX was installed with.
|
|
|
|
A client that opens a connection and then stalls holds one serving context until it gives up, so
|
|
the proxy should time out slow requests (`client_header_timeout` and `client_body_timeout` in
|
|
nginx) and buffer responses, which also frees a context as soon as a slow download is handed to
|
|
the proxy.
|
|
|
|
## Trust
|
|
|
|
* **The repository key stays offline.** The service holds only the repository's public key
|
|
(`repoKey`). The operator signs an approved package on their own computer
|
|
(`tools/review.lua`), and the service checks that signature with the package tool before it
|
|
publishes the version. A stolen server can withhold or withdraw packages, but it cannot make one
|
|
DVX will install.
|
|
* **Every upload is checked by the package tool** (`dvxpkg check`, the same code DVX installs
|
|
with): the manifest, every file against it, and the rules for where each kind of package may put
|
|
its files. Nothing a manifest says is taken on the uploader's word.
|
|
* **Publishers may sign their own packages** with their own keys (`dvxpkg keygen`, or the Package
|
|
Builder). The catalogue shows each version's publisher key, if it has one, so a user can tell two
|
|
publishers apart.
|
|
* **An id belongs to the account that first uploaded it.** Another account's upload of the same id
|
|
is refused, as is a second upload of a version already taken. Ids of the vendor `kpunch` are
|
|
DVX's own and only the operator uploads them.
|
|
|
|
## The API
|
|
|
|
All JSON except the downloads. Every reply carries `X-Request-Id`, which is also in the log.
|
|
|
|
| Method | Path | What it does |
|
|
|---|---|---|
|
|
| GET | `/v1/health` | Liveness and the schema version. |
|
|
| GET | `/v1/catalogue` | Every published package at its newest approved version: id, version, name, kind, summary, publisher, author, licence, homepage, dvx, abi, size, sha256, signer, signed, and depends (`[{id, min}]`). No session. |
|
|
| GET | `/v1/package/<id>` | One package's approved versions, newest first. |
|
|
| GET | `/v1/download/<id>/<version>` | The bytes, with `X-Content-Sha256`. Resumable: `Range` is answered. |
|
|
| GET | `/v1/signature/<id>/<version>` | The repository's signature of that version (a `NAME.SIG` file). |
|
|
| POST | `/v1/updates` | `{dvx, abi, installed: [{id, version}]}`, the client's own list: answers `updates` (the newer versions) and `unknown` (ids the catalogue does not have). Nothing of it is kept. |
|
|
| POST | `/v1/upload` | The `.DPK` as the body, from a signed-in account. Taken in as a pending version for review. DVX's Software sends it (File > Publish a Package) with the account its settings give. |
|
|
| GET | `/v1/mine` | The account's own packages, every version with its review state and note. |
|
|
| GET | `/v1/admin/pending` | Operator. Versions waiting for a review, oldest first. |
|
|
| GET | `/v1/admin/download/<id>/<version>` | Operator. Any version's bytes, to review and sign. |
|
|
| POST | `/v1/admin/version/state` | Operator. `{package, version, state: approved or rejected, signature, note}`. Approving needs the repository's signature of the version. |
|
|
| POST | `/v1/admin/package/state` | Operator. `{package, state: published or withdrawn}`. |
|
|
| GET | `/v1/admin/log` | Operator. The log, searched by any field (as singemaster's). |
|
|
| POST | `/v1/admin/key/grant`, `/v1/admin/key/revoke` | Operator. `{account, key}`; the key `admin` makes an administrator. |
|
|
|
|
The account routes (`/v1/account/register`, `verify`, `login`, `logout`, `recover`, `reset`, and
|
|
`GET /v1/account`) are singemaster's, with mail that names DVX.
|
|
|
|
The operator is the console token (`adminToken`) or the session of an account holding the `admin`
|
|
key. Withdrawing a package stops it being listed and downloaded; the copies installed keep working
|
|
and simply stop being offered updates.
|
|
|
|
## Serving contexts
|
|
|
|
A DOS machine downloads slowly. The main context listens and does the housekeeping (queued mail,
|
|
expired accounts); `serveContexts` serving contexts, each with its own database connection and
|
|
routes, accept on its listener, so one slow download holds up only the context sending it. Each
|
|
connection goes to one of them; with every one busy, new connections wait in the system's queue
|
|
until one is free.
|
|
|
|
## Reviewing uploads
|
|
|
|
`tools/review.lua` runs on the operator's own computer, the one place the repository's secret key
|
|
is kept. It needs singemaster's `lib/config.lua` (`singe=DIR` when singemaster is not beside this
|
|
repository) and the package tool from a DVX build or the SDK.
|
|
|
|
calog tools/review.lua -- review.conf pending
|
|
calog tools/review.lua -- review.conf fetch acme.cards 1.0.0
|
|
calog tools/review.lua -- review.conf approve acme.cards 1.0.0 "looks fine"
|
|
calog tools/review.lua -- review.conf reject acme.cards 1.1.0 "the help file is missing"
|
|
|
|
`review.conf` holds `server` (the service's public address), `token` (the console token or an
|
|
administrator's session), `dvxpkg`, `secret` (the repository's secret key), and `work` (where
|
|
fetched packages and their signatures are kept). `fetch` checks the download against the digest the
|
|
service sent and shows the manifest and files as the package tool reads them. `approve` signs the
|
|
fetched file itself, so what is approved is exactly what was reviewed; if that file has changed
|
|
since, the service refuses the signature. `withdraw` and `publish` take a package off the catalogue
|
|
and put it back.
|
|
|
|
## Publishing DVX itself
|
|
|
|
`make` in the DVX tree writes `dist/DVXUPD.DPK`: DVX itself as the package `kpunch.dvx` (kind
|
|
`system`), every file of the release but the optional fonts, at this DVX's version and ABI. Packages
|
|
of the vendor `kpunch` are the operator's, so it is uploaded with the console token and approved
|
|
like any other:
|
|
|
|
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/octet-stream" \
|
|
--data-binary @dist/DVXUPD.DPK https://packages.example.com/v1/upload
|
|
calog tools/review.lua -- review.conf approve kpunch.dvx 1.0.1
|
|
|
|
Software then lists it first on its Updates page. DVX installs it only when the repository's
|
|
signature checks against the key in its own `SYSTEM\KEYS`, and names the add-ons a new ABI would
|
|
leave unable to load before anything is installed.
|
|
|
|
The same build writes DVX's own add-ons to `dist/addons/` (`src/addons/README.md`): instrument
|
|
banks for MIDI music, kept out of the base install. They are uploaded and approved the same way:
|
|
|
|
for dpk in dist/addons/*.DPK; do
|
|
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/octet-stream" \
|
|
--data-binary @"$dpk" https://packages.example.com/v1/upload
|
|
done
|
|
calog tools/review.lua -- review.conf approve kpunch.dmxopl 1.11.0
|
|
calog tools/review.lua -- review.conf approve kpunch.dmxopl3 2.11.0
|
|
|
|
## Configuration
|
|
|
|
`dvx.conf.example` lists every key with a comment. Beyond singemaster's (`bindHost`, `bindPort`,
|
|
`publicOrigin`, `dataDir`, `database`, `pbkdf2Iterations`, `mailFrom`, the relay settings, and
|
|
`adminToken`), the server needs `dvxpkg` (the package tool), `repoKey` (the repository's public
|
|
key), `serveContexts`, and `uploadMax` (the largest upload in bytes).
|
|
|
|
## Tests
|
|
|
|
test/run.sh
|
|
|
|
Needs `bin/host/dvxpkg` from a DVX build, calog, and singemaster beside this repository. It makes
|
|
its own keys and packages with the package tool, starts the service on a scratch database, and runs
|
|
two suites: `testPackages` (uploads checked and refused for the right reasons, review with the
|
|
repository's signature, the catalogue, resumed downloads that DVX would trust, updates, withdrawing,
|
|
an account's own packages, and a slow client not holding up the others) and `testReview` (the review
|
|
tool end to end, including a changed file whose signature is refused). Then it starts singemaster
|
|
and this service in one calog process and checks that both answer.
|
|
|
|
test/serve.sh DATA PORT FIXTURES DVXPKG
|
|
|
|
The package server for DVX's DOS tests: the DOS test harness runs it for a script that says
|
|
`# REQUIRES: PKGSERVER`. It makes a test repository key, gives the test DVX that key, a record of
|
|
DVX itself, and a theme to update (installed with the package tool), uploads and approves the
|
|
fixture packages and the next version of DVX with `tools/review.lua`, and serves them on
|
|
`127.0.0.1:PORT`, which the emulator's guest reaches as `10.0.2.2`.
|