Skip to content

Begin typing to search this documentation.

Project schema

Pre-release interface

Hoplite consumes a normal Hara project containing source paths, a main namespace, capabilities, and profiles. The selected profile must use :profile/language :hoplite.

Field Type Required Validation
:profile/language keyword Yes Must equal :hoplite
:profile/main qualified symbol Yes Must evaluate to a tagged application or internal config
:profile/options map No Non-map values are rejected
:profile/extensions map No Host-owned configuration; Hoplite reads :extension/hoplite
:profile/options :port integer No Must be between 1 and 65535; defaults to 8080
:profile/options :workers positive integer No Defaults to 1 in development and available CPU count in production

Optional package activation remains inside project.edn:

{:profile/extensions
{:extension/hoplite
{:hoplite/modules
[{:module/id "gh:example:service-package"
:module/version "0.1.0"
:module/export :example/service
:module/as :service
:module/config {}}]}}}

The canonical Hoplite package coordinate is gh:greenways-ai:hoplite. gh:OWNER:REPOSITORY coordinates resolve .harp archives from GitHub releases. Every activation selects one qualified :module/export, binds it to a unique local :module/as alias, and uses an exact SemVer. A package may be activated more than once when selecting different exports. Configuration must be inert EDN without reader tags or metadata.

During hoplite serve check and hoplite serve build, Hoplite verifies the locked archive, resolves the selected export’s :export/namespace, follows only package-local :require edges, and compiles the resulting namespace closure into the application HBX0 bundle. Package source is a build input and is not reopened by hoplite-server during production startup.

Package resolution binds the coordinate, release asset, export, version, archive digest and identity revision to project.lock.edn; Hoplite does not perform live package lookup at production runtime. Storage and other native authority are selected by the distribution, not by application request data.

The default build rejects :hoplite/authentication. Account, session, and authorization policy belong to the application or an explicitly composed distribution; see Application authentication.

Every explicit module must have an exact archive and namespace inventory in project.lock.edn:

{:lock/format "0.0.0-alpha"
:packages
{"gh:example:service-package"
{:version "0.1.0"
:tap "gh"
:registry-commit "0123456789abcdef0123456789abcdef01234567"
:identity-revision "89abcdef0123456789abcdef0123456789abcdef"
:archive-sha256 "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
:namespaces [example.service]}}}

The commit fields are lowercase 40-character Git revisions. The namespace inventory must be non-empty and include the namespace selected by every active export. Two locked packages cannot claim the same namespace.

The requested version, lock entry, installed registration, content-addressed root name, HARP identity, selected export, namespace inventory, and every file digest must all agree before module code is evaluated.

Hoplite does not load a root hara.extension.edn. HTA or WASM extension contracts belong under the applicable profile’s :profile/extensions map, alongside :extension/hoplite. A project containing the legacy root file fails validation with a migration message instead of silently combining two sources of truth.

Field Type Required Behavior
:name text-like No Defaults to app-<id>
:handler callable or Var Unless resources or generated channel hooks Creates one ANY /*path catch-all route
:resources sequence Unless catch-all Flattened into method/path/handler routes
:route/adapter keyword No Default route boundary: :raw, :request (default), or :request+hta
:request/body map No Enables a bounded native request-body handle for the application
:proxies sequence No Closed fixed-prefix, fixed-origin Nginx upstream declarations
:channels sequence No Values constructed with hoplite.nchan/channel; hoplite.core/channel remains compatible
:peers sequence No Values constructed with hoplite.core/peer
:openapi :path text-like No Explicit OpenAPI route; development otherwise defaults to /openapi.json

Each resource operation accepts the same :route/adapter field and overrides the application default for that route. The selected value is recorded in the version 2 apps.hta manifest.

The closed :request/body map requires positive integer :max-bytes and may include positive :max-chunk-bytes. The chunk limit defaults to the smaller of 64 KiB and the total limit and cannot exceed :max-bytes. An application with this profile cannot contain :request+hta routes. Native-body handlers receive an opaque :body-handle; the handle is scoped to the owning request and work.

:route/auth is not a Hoplite transport field and is rejected. Authorization must run inside the HAL handler.

Experimental

Every item under :channels must be constructed with hoplite.nchan/channel. hoplite.core/channel remains a compatibility constructor for the current pre-1.0 line. The map is closed; unsupported keys are errors.

Field Type Required Default and validation
:name text-like Yes 1–64 ASCII letters, digits, -, or _; unique per app
:path text-like Yes Safe absolute path ending in /:channel; unique and non-overlapping with proxies
:profile keyword No Defaults to and must equal :ephemeral
:authorize callable or Var Yes Prepared as the subscriber-authorization handler
:admit callable or Var Yes Prepared as the publisher-admission handler
:transports sequence of keywords No Defaults to [:websocket :sse]; only those values; non-empty
:message-buffer integer No Defaults to 8; range 0–64
:message-timeout-seconds integer No Defaults to 30; range 1–300
:max-channel-id-bytes integer No Defaults to 128; range 22–256
:max-subscribers integer No Defaults to 4; range 1–64

Hoplite generates private GET and POST routes for Nchan authorization subrequests. They are prepared like other handlers and recorded in the runtime manifest, but omitted from public OpenAPI output. The application package cannot inject raw Nginx directives or replace the statically linked Nchan module.

Experimental

Every item under :peers must be constructed with hoplite.core/peer. The map is closed; unsupported keys are errors.

Field Type Required Default and validation
:name text-like Yes 1–64 ASCII letters, digits, -, or _; unique per app
:channel text-like Yes Must name a channel in the same application
:handler callable or Var Yes Application-owned peer policy handler
:label text-like No Defaults to peer name; 1–64 ASCII letters, digits, -, _, or .
:max-message-bytes integer No Defaults to 65536; range 1–1048576
:idle-timeout-seconds integer No Defaults to 120; range 5–3600

Peer declarations do not create portable session identities. Runtime handles created through hoplite.rtc stay inside one Nginx worker.

Field Type Required Behavior
:worker-processes positive integer No Overrides profile/default worker count
:apps non-empty sequence Yes Application instances to host

Instance fields are documented in Multiple applications.

Sources: core/src/platform.rs, core/src/package_catalog.rs, and core/src/app.rs