- Larva
- Cómo funciona Larva
- Internos de Hive
Esta página aún no está traducida al español. Leer la versión en inglés
Hive internals
How Hive packages are laid out, signed, verified, resolved, installed and served - the .hive format, hive.json, signing keys, the cache, hive.lock and the registry protocol.
En esta página
- The pieces at a glance
- The .hive file
- Paths inside a package
- Folders inside a package
- package.queen
- Limits
- hive.json
- Signing keys
- Opening a package
- Packing
- The cache
- Trusted keys
- Choosing versions
- Installing
- hive.lock
- Building with packages
- Registries
- Providers
- The index
- Publishing rules
- The HTTPS protocol
- Settings that affect Hive
Hive is Larva's package manager. Packages with Hive explains how to use it; this page explains how it works. Language packs have a separate registry with its own rules, described in The languages registry.
| Part | Where | What it does |
|---|---|---|
Hive.Core |
HiveArchive |
The .hive file: writing, opening and checking, unpacking. |
HiveManifest |
hive.json: the manifest record, its JSON form and its checks. |
|
HiveKeys |
Signing keys: generation, fingerprints, signing and verification. | |
HivePacker |
Turning a library project into a package. | |
HiveCache |
Installed packages, trusted keys, and hive.lock. |
|
HiveInstaller |
Choosing versions, fetching, checking and unpacking; HiveResolver for builds. |
|
HiveRegistry |
The registry index, and the folder and HTTPS providers. | |
Larva.Build |
QueenPackages |
PackageReference, VersionRange and the HIVE chunk (HiveSettings). |
Larva.Cli |
HiveCommand |
larva hive and its subcommands. |
Hive was once a tool of its own; it is now larva hive, and Hive.Core is a library inside larva. Because larva is compiled ahead of time, Hive.Core writes and reads JSON through a source-generated serializer context (HiveJson) instead of reflection.
The pieces at a glance
library project (.queen) program project (.queen)
│ larva hive pack │ DEPS package: Ana/Greetings@^1.0
▼ │
bin/Greetings-1.0.hive ── larva hive publish ──► registry │ larva hive install
(zip: hive.json, Ana/Greetings/index.json
hive.sig, files) Ana/Greetings/1.0/Greetings-1.0.hive
│
download, check hash, │ signature, every file
▼
cache: packages/Ana/Greetings/1.0/ ──► hive.lock
│
larva build / run / lsp (HiveResolver, no network)
▼
package.queen loaded as a dependency project
Installing is the only step that touches the network. Building only looks in the cache.
The .hive file
A package is a zip archive. HiveArchive.Create writes its entries in a fixed order:
| Order | Entry | What it is |
|---|---|---|
| 1 | hive.json |
The manifest. |
| 2 | hive.sig |
The signature of the manifest's exact bytes. Present only when the package is signed. |
| 3… | the files | Exactly the files the manifest lists, in ordinal order of their path. |
Every entry is written with the strongest compression and the same time stamp, 2000-01-01 00:00 UTC, so the same inputs give the same archive apart from the signature (ECDSA signatures are randomized: packing twice gives an identical hive.json and a different hive.sig). The archive is first written to <name>.partial and then moved into place.
The package of a small library, Ana/Greetings 1.0, compiled and signed:
unzip -v bin/Greetings-1.0.hive
Archive: bin/Greetings-1.0.hive
Length Method Size Cmpr Date Time CRC-32 Name
-------- ------ ------- ---- ---------- ----- -------- ----
562 Defl:X 321 43% 2000-01-01 00:00 bbe89ded hive.json
70 Defl:X 75 -7% 2000-01-01 00:00 2153b175 hive.sig
4096 Defl:X 1972 52% 2000-01-01 00:00 4886d1a2 lib/Greetings.dll
173 Defl:X 132 24% 2000-01-01 00:00 89a4682b package.queen
-------- ------- --- -------
4901 2500 49% 4 files
Paths inside a package
HiveArchive.SafePath accepts a path only if it is relative, uses /, is at most 240 characters, and each part is non-empty, not . or .., does not end in a dot or a space, has no control characters or < > " | ? *, and is not a Windows device name (CON, PRN, AUX, NUL, COM1…, LPT1…). hive.json and hive.sig themselves cannot be package files. The same check runs when packing, when parsing a manifest, before downloading a file named in an index, and once more when unpacking (every target must stay under the package's folder).
Folders inside a package
HivePacker decides where each file goes:
| Folder | Contents |
|---|---|
lib/ |
The compiled library (Name.dll, a Larva library), the Larva libraries it uses (assembly: in DEPS), and any .lvo objects (larvaobj:). |
src/ |
With publish: source: the project's sources, at their paths under the project. |
src/shared/<8 hex>/ |
Sources from outside the project's folder, grouped by a hash of their folder. |
native/ |
Native libraries (lib: in DEPS). |
named/ |
Resources named by a key (logo: images/logo.png). |
| top level | Resources named by their path (embed:), at that path; and package.queen. |
A resource whose path would land in lib/, src/ or native/, or be package.queen, is refused. Two different files that would get the same path are refused.
package.queen
Every package carries a generated package.queen: the package described as a library project. This is the file a program compiles with once the package is installed, so an installed package is just another dependency project to Larva.Build (see Projects and builds). The one in Ana/Greetings:
queen 1.0;
chunk PROJ:
name: Greetings,
type: library,
version: v1.0,
author: Ana
chunk SOURCES:
scan: false
chunk DEPS:
assembly: "lib/Greetings.dll"
The packer adds what applies: larvaobj: lib when there are .lvo objects, an assembly: per Larva library, a lib: per native library, a package: per package the project depends on, a RESOURCES chunk, and for a source package include: src/** in SOURCES. scan: false keeps the package's folder from being searched for sources.
Limits
| Limit | Value | Checked |
|---|---|---|
| Files in a package | 20 000 (MaxFiles), plus the manifest and signature |
before reading any entry |
| Total unpacked size | 512 MiB (MaxUnpacked), as the manifest declares |
before reading any file |
hive.json |
1 MiB | while reading it |
hive.sig |
4096 bytes | while reading it |
| Each file | the size its manifest entry declares | while decompressing: the real byte count is limited, not the size the zip header claims |
| A download | 512 MiB; an index, 16 MiB | while reading the HTTP body |
Counting real bytes while decompressing is what stops a zip bomb: an entry that claims to be small and expands to gigabytes is cut off at its declared size.
hive.json
HiveManifest is written as indented camelCase JSON with null fields left out. The manifest of the package above:
{
"format": 1,
"package": "Ana/Greetings",
"version": "1.0",
"license": "MIT",
"kind": "compiled",
"gc": "default",
"dependencies": [],
"files": [
{
"path": "lib/Greetings.dll",
"size": 4096,
"sha256": "983c3a1998709c4e3c0fb2173abda5e480ed38e22e51805e26713e8bcc900f8a"
},
{
"path": "package.queen",
"size": 173,
"sha256": "945759d1090a816a3f985ffc1e135178ea22466c394dcb6ca6555d3a649545a3"
}
],
"key": "633d6fd406a21ba62eb5d91ee29d3202c2020ea8958c308bb242ea4445a62502"
}
| Field | Meaning |
|---|---|
format |
The manifest format, 1. A reader refuses a higher one and asks for a newer Hive. |
package |
Author/Name, without a version. Each part: 1 to 64 ASCII letters, digits, _, - or ., starting with a letter or digit, no ... |
version |
The exact version, dot-separated u32 terms (1.0, 3.5.0). |
description, license |
From PROJ's description and HIVE's license. |
kind |
compiled or source. |
gc |
For a compiled package, the GC mode of the project that packed it. Absent for a source package. |
dependencies |
[{ "package": "Author/Name", "version": "^1.2" }], from the project's package: entries; version is empty for "any". |
files |
Every file the package carries, with its size and lowercase hex SHA-256. |
key |
The fingerprint of the signing key, or absent when the package is unsigned. |
Because the signature covers the manifest and the manifest lists the hash of every file, one signature vouches for the whole package. HiveManifest.Parse also rejects unsafe paths, sizes below zero, hashes that are not 64 characters, a version that is a range, and dependencies that do not parse.
The manifest's gc is informational today: the generated package.queen has no BUILD chunk, so a program that installs the package loads it with the program machine's defaults, and a package built for another GC is not linked as a separate GC domain.
Signing keys
HiveKeys uses ECDSA over the NIST P-256 curve with SHA-256.
| Item | Form |
|---|---|
| Private key | A PEM file, PKCS #8 (-----BEGIN PRIVATE KEY-----), written by larva hive key. About 240 bytes. |
| Public key | The DER SubjectPublicKeyInfo, base64. It travels in the registry's index and in the Hive-Public-Key header. |
| Fingerprint | The SHA-256 of the public key's DER bytes, 64 lowercase hex digits. It goes in hive.json, the index and hive.lock. Messages show its first 16 digits. |
| Signature | hive.sig: the DER-encoded (RFC 3279) ECDSA signature of the exact bytes of hive.json, about 70 bytes. |
Signing the bytes rather than a re-serialized object means a verifier never has to reproduce the writer's JSON formatting: it hashes what is in the archive.
larva hive key
…
public key: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWcf85mJcX1Ivape5QdiHiXQmf+fLSA0V50VtTET1Js/BEGxUA+UZamc1VGfyHSawktSeUaKZoRajUPTINWy12Q==
fingerprint: 633d6fd406a21ba62eb5d91ee29d3202c2020ea8958c308bb242ea4445a62502
The … line names the file written (hive.key by default, or -o <file>). An existing file is never overwritten. The project names its key with sign: in HIVE; larva hive pack reads it, computes its public key and fingerprint, puts the fingerprint in the manifest, then signs.
Opening a package
HiveArchive.Open(bytes, publicKey, requireSignature) checks everything before anything is written to disk:
- The bytes are a zip archive, with at most
MaxFiles + 2entries. hive.jsonexists and parses (see above).hive.sigis read if present. Without it, the package is refused when a signature is required or a key is given.- With a key: the manifest's
keymust equal the key's fingerprint, and the signature must verify over the manifest bytes. - The declared total size is within
MaxUnpacked. - Every entry other than the manifest and signature must be listed in the manifest, have exactly its size and SHA-256, and appear once. Empty folder entries are ignored.
- Every listed file must be present.
Extract then writes the files into <folder>.partial, adds hive.json and hive.sig, deletes any previous <folder>, and renames .partial into place. An interrupted install leaves a .partial folder that the cache ignores and the next install replaces.
Installing runs nothing from the package: there are no install scripts, and the files are only read by later builds.
larva hive verify runs Open on a file. Without --key it reports who signed but cannot check it; with --key the signature must be that key's:
larva hive verify bin/Greetings-1.0.hive
Ana/Greetings 1.0 (compiled, gc default): 2 files, 4269 bytes
signed by 633d6fd406a21ba6 (not checked: give --key)
every file matches its hash
larva hive verify bin/Greetings-1.0.hive --key MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWcf85mJcX1Ivape5QdiHiXQmf+fLSA0V50VtTET1Js/BEGxUA+UZamc1VGfyHSawktSeUaKZoRajUPTINWy12Q==
Ana/Greetings 1.0 (compiled, gc default): 2 files, 4269 bytes
signature good (633d6fd406a21ba6)
every file matches its hash
The same package checked against another key, and two altered copies (one with a byte of package.queen changed, one with a file the manifest does not list):
larva hive verify bin/Greetings-1.0.hive --key MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKo2b3Tsz7CQTCYXoU6YAY6pU3eSB0xSv+90nRqXnmy2OuUU+vT/MZZayBCo10JGKxaegfkdyIrIo1Im2yaO2pw==
larva hive: Ana/Greetings 1.0 says it was signed by key 633d6fd406a21ba6, not by 6386be6f2911c074
larva hive verify bin/tampered.hive
larva hive: `package.queen` in Ana/Greetings 1.0 does not match its hash: the package is damaged or was changed
larva hive verify bin/extra.hive
larva hive: the package carries `lib/extra.dll`, which its manifest does not list
Packing
larva hive pack [project] (HiveCommand.Pack, then HivePacker.Pack):
- Loads the project. It must be
type: library, have aversion, and not beprivate: true. - Picks the package name:
HIVE'spackage:, elsePROJ'sauthor(spaces become-) andname. - For a compiled package with sources, runs
larva libinto a temporaryhive-<guid>folder, against the project'sassembly:libraries, givingName.dll. - Collects the files (see the folders), generates
package.queen, fills the manifest, signs it ifsign:is set, and writesbin/<Name>-<version>.hive.
larva hive pack
…
2 files, 4269 bytes before compression
…
The elided lines are larva lib's report for the temporary Greetings.dll, and packed <path>\bin\Greetings-1.0.hive (2920 bytes). An unsigned package gets a note that projects with verify: strict will refuse it.
A project with scan: false and no sources (a package around a binding built elsewhere) skips step 3; its assembly: libraries are the code. A package with nothing compiled and no sources is refused.
The library is compiled with the machine's frontend options (larva config), not the project's access.default or borrowcheck. Its resources are not compiled into Name.dll: they travel as files and the generated package.queen embeds them into the program that uses the package.
The cache
HiveCache.Default() is HIVE_CACHE, else %LOCALAPPDATA%\Larva\hive. A project's cache: in HIVE replaces it for that project.
<cache>/
keys.json trusted keys: { "Ana/Greetings": ["633d6fd4…"] }
packages/
Ana/
Greetings/
1.0/
hive.json the manifest, as it was in the archive
hive.sig its signature (when signed)
.sha256 hash of the .hive file it came from
package.queen
lib/Greetings.dll
1.1.partial/ an interrupted install: ignored
A version counts as installed when its folder has both package.queen and hive.json, and its name parses as a version. Versions() lists them newest first; larva hive list prints every one.
Trusted keys
keys.json maps each package to the fingerprints allowed to sign it. HiveCache.Trust runs on every install of a signed version:
- the first time a package is installed, its signer is remembered;
- later, a version signed by a remembered key is accepted;
- a version signed by any other key is refused, with a message that names
larva hive trust Author/Name <fingerprint>.
larva hive trust only adds a fingerprint to the list. This is "trust on first use": a registry that starts serving a package under a new key cannot make an existing installation accept it silently.
Choosing versions
A reference is Author/Name[@range]; VersionRange.Parse reads the range:
| Written | Operator | Accepts |
|---|---|---|
1.2, =1.2, v1.2 |
Exact |
versions equal term by term (missing terms count as 0, so 1.2 equals 1.2.0) |
^1.2 |
Compatible |
at least 1.2, with the same terms up to and including the first non-zero one: ^1.2 is 1.x, ^0.3 is 0.3.x, ^0.0.4 is 0.0.4 |
>=1.2 |
AtLeast |
1.2 or later |
no @ |
none | any |
HiveInstaller.Choose finds one version per package by iterating to a fixed point:
chosen := {}
repeat (at most 100 rounds):
requirements := the project's package: entries
+ the dependencies of every version in chosen
for each package with requirements:
fitting := index versions accepted by every requirement,
not yanked (unless it is the version hive.lock pins)
if fitting is empty: fail, listing who wants what and what is published
next[package] := the pinned version if it fits, else the newest fitting
if next == chosen: done
chosen := next
The dependencies of a version are read from the registry's index, so choosing needs no package downloads. Each package's index comes from the first provider in the list that has it. The result is a fresh hive.lock holding only the packages still needed.
larva hive restore does not choose: for each locked entry it finds that version in the index, requires its SHA-256 to equal the lock's, and installs it.
Installing
HiveInstaller.Fetch installs one chosen version:
- If the cache already has that version and its
.sha256equals the index's hash, nothing is done. - Download the file and compare its SHA-256 with the index's. A mismatch is refused.
- If the version is signed, take its public key from the index's
keys, and check that the key's fingerprint is the one claimed. HiveArchive.Openwith that key: signature and every file. A signature is required unless the project saysverify: hashes.- The manifest's package and version must be the ones asked for, so a registry cannot answer one name with another package.
HiveCache.Trustwith the signer's fingerprint.Extractintopackages/Author/Name/version, then write.sha256.
larva hive add first adds a package: line to the DEPS chunk of the .queen file (creating the chunk at the end when there is none) and then installs. Both add and install read the project without resolving packages, since those are what is being installed.
hive.lock
hive.lock is JSON beside the .queen file (HiveLock), its packages sorted by name:
| Field | Meaning |
|---|---|
format |
1 |
packages[].package |
Author/Name |
packages[].version |
the exact version chosen |
packages[].sha256 |
the SHA-256 of the .hive file |
packages[].key |
the signer's fingerprint, or absent |
packages[].provider |
the registry it came from |
packages[].dependencies |
that version's dependencies, as in the manifest |
Building with packages
larva build, larva run, larva show/types and the language server pass HiveResolver.Create() to QueenProject.Load. The resolver never downloads:
- The first project that asks (the one being built) decides the cache and the lock for the whole tree.
- For each
package:, it lists the installed versions that the range accepts. It takes the versionhive.lockpins when that one is installed, else the newest. - It returns that version's
package.queen, whichQueenProjectloads as a dependency project. A package that is not installed is errorQN108, which suggestslarva hive install(orlarva hive restorewhen the lock pins it).
From there a package is an ordinary dependency: its assembly: libraries are referenced and linked, its lib: files are copied next to the program, its resources are embedded, and a source package's sources are compiled with the program.
Registries
Providers
A project's provider: entries (or hive.provider from larva config, or antril.org/hive when neither is set) are parsed by HiveProvider.Parse, relative to the project's folder:
| Written | Provider |
|---|---|
file:<path> |
a folder |
https://… |
HTTPS |
http://… |
HTTP, accepted only for a loopback address |
an existing folder, or a path starting with . or rooted |
a folder |
anything else (antril.org/hive) |
https:// + the text |
Installing tries the providers in order for each package. Publishing goes to the first one, or to --provider.
The index
A registry is only files, so a folder, a static web server or the site's API can all be one. For a package Author/Name it has:
{root}/Author/Name/index.json
{root}/Author/Name/{version}/{Name}-{version}.hive
index.json (HiveIndex):
| Field | Meaning |
|---|---|
package |
Author/Name. An installer refuses an index for another package. |
keys |
{ fingerprint: public key (base64) }: the keys allowed to sign this package. |
versions[].version |
the exact version |
versions[].file |
the package file, relative to the package's folder: {version}/{Name}-{version}.hive |
versions[].sha256 |
the SHA-256 of that file |
versions[].key |
the signer's fingerprint, or absent |
versions[].dependencies |
as in the manifest |
versions[].yanked |
withdrawn: installed only when a lock already pins it |
Versions are kept sorted, oldest first.
Publishing rules
HiveProvider.Add updates an index, and the rules are the same in a folder registry and on the server:
- A version that exists cannot be published again.
- The first version of a package brings its key into
keys. - Later versions must be signed by a key already in
keys. A new key is accepted only for a package that has none. - Once a package has a key, an unsigned version is refused.
So a registry cannot be used to switch a package's signer quietly, and installers that remember keys add a second line of defense.
larva hive publish packs, opens the result to read its manifest, and calls the provider's Publish with the file, its hash, the manifest's key and dependencies, and the public key derived from sign:. A folder provider writes the file and replaces index.json through a .partial file.
The HTTPS protocol
HiveHttpProvider speaks plain HTTP requests over HTTPS:
| Request | Answer |
|---|---|
GET {root}Author/Name/index.json |
200 with the index; 404 when the registry does not have the package (the installer then tries the next provider). |
GET {root}Author/Name/{version}/{Name}-{version}.hive |
200 with the package file. |
PUT {root}Author/Name/{version} |
Publishes. The body is the .hive file; headers Authorization: Bearer <HIVE_TOKEN>, Hive-Sha256: <hex> and, for a signed package, Hive-Public-Key: <base64>. |
Any status other than success or 404 is an error, shown with its reason phrase. Requests time out after two minutes, and response bodies are limited while they are read.
The public registry at https://www.antril.org/hive-api implements the same protocol with Hive.Core itself. Its PUT answers:
| Status | When |
|---|---|
201 |
Published; the body is { "package", "version", "file", "sha256", "key" }. |
400 |
An empty body, a bad name or version, a missing or wrong Hive-Sha256, or a Hive-Public-Key that is not a key. |
401 |
No valid API token. |
403 |
The author name is not yours, or the key rules above refuse the version. |
409 |
That version is already published. |
413 |
The package is larger than the registry accepts. |
422 |
The archive does not open (a bad signature, a file that does not match its hash…), its manifest names another package or version, or it is signed and no public key was sent. |
Errors come as plain text, also placed in the reason phrase so that larva hive can show them. Package files are served with Cache-Control: public, max-age=31536000, immutable, since a published version never changes; indexes with no-cache.
Not implemented yet: yanking a version (the index has the yanked field and installers honor it, but no command or API sets it).
Settings that affect Hive
| Setting | Effect |
|---|---|
HIVE provider |
Registries, in order. |
larva config hive.provider |
Registries for projects that name none (comma-separated). Not a BUILD key. |
HIVE verify |
strict (default): every installed package must be signed. hashes: unsigned packages are accepted; a version the index says is signed is still checked against its key. |
HIVE cache |
The cache folder for this project. |
HIVE sign, package, license, publish, private |
Packing and publishing, as described above. |
HIVE_CACHE |
The user's cache folder. |
HIVE_TOKEN |
The API token sent when publishing over HTTPS. |
Editar esta página en GitHub Reportar un problema