Rho Rho language and toolchain

Remote module registry for Rho

The Rho module registry

The registry is the service a Rho installation asks where a module comes from:
rho module search, rho module install and rho module publish all speak
HTTP to a registry, and the registry this repository ships is one.

It is an application rather than a component, because it has a main and
generates a program. It generates the Go service target alone: a registry is
only useful behind a network, so there is no native build.

What it does

OperationRouteWhat it does
PublishPOST /api/modules/{name}/{version}Accepts a package and records its metadata
DownloadGET /api/modules/{name}/{version}Serves the published package
DetailGET /api/modules/{name}Lists a module's versions, newest first
SearchGET /api/search?q=…Searches by name and description
HealthGET /api/healthReports the service and its version

The pages around those routes are HTML: an index of published modules, a page
per module, a page per version, and a not-found page that is styled rather than
bare, because a reader who mistypes a name is still a reader.

Access

A registry is open by default, which suits a trusted network, and a deployment
that needs it secured sets one setting:

# registry.conf
access = secured

With access = secured, publishing requires a token. Tokens are issued by the
credential manager, expire, and have a ceiling: a token can never outlive
token_max_days, whatever the settings say.

Storage

What a registry knows is kept in tables, declared in db/schema.yml and created
when the service starts:

TableWhat it holds
usersthe identities, named by whatever credential presented them
rolesthe named permissions an identity may hold
user_roleswhich roles an identity holds
tokensthe credentials the service has issued, and which have been withdrawn
modulesthe names it holds, who owns each, and whether each is public
module_maintainerswhich identities may publish to a module, and as what
module_versionsthe published versions, each with its package and its manifest

A publish is therefore one commit: the upload and the record of who made it are
the same statement. A registry that wrote a package to a file and then wrote the
record beside it could be interrupted between the two, and what is left is a
module nobody owns.

What stays on the filesystem is what is derived rather than recorded — the
source tree a package is unpacked into, and the documentation generated from it:

<data>/registry.db                                 the records
<data>/docs/<name>/<version>/                      the generated pages
<data>/modules/<name>/<version>/src/               the unpacked package

The database sits beside the data directory rather than inside the served tree,
and is named by RHO_SQL_DATABASE — defaulting to <data>/registry.db — while
RHO_SQL_DRIVER names the driver, defaulting to sqlite. Both are the
deployment's to choose, because a binary that compiled either in would be a
binary this build decided them for.

The data directory is separate from the application so that installing a new
version over the old one cannot take the modules with it. RHO_REGISTRY_DATA
overrides it, which is what a container uses.

The tables are declared rather than written as SQL: adding a column is a change
to one file, and the schema the service creates and the code generated from it
come from the same reading of it. The generated Rho is an output of the build
and is not checked in.

Building it

rho build applications/registry

That generates the Go service under build/generated/go-server/ and builds the
binary. The service carries its own templates, its own static files and the log
port, so the result is one binary with no runtime dependencies beyond the C
library.

Where to go next

  • handbook/documentation — how this page and the one beside it are
    produced, and how a module documents itself the same way.
  • The deployment note beside this page, for running it.