Facade for the OPAQUE addon seam: mount a compiled, source-free IAddon.
The problem this solves is commercial, not technical. A proprietary addon
cannot ship as a jar (every namespace name, every function name and the whole
call graph read straight out of it) and cannot ship as source at all. It
ships as ONE native binary with an embedded bytecode payload, built by
cljw build from the vendor's own code written against this library's
portable stratum, and the host mounts it through the generic proxy here.
Nothing in the mount path is special-cased for it. An opaque addon is an ORDINARY mount manifest with :addon/type :external and :addon/trust-class :proprietary, so the existing licence gate (hive-addon.mount.entitlement) governs it, and an unlicensed addon's constructor namespace is never loaded.
The vendor's side of the same wire is hive-addon.opaque.serve, and
entry-source writes the entry that calls it, so the kernel and the proxy
are framed by one library and cannot drift.
JVM-only (.clj): it reaches the subprocess transport and writes manifest files. The strata below it are .cljc, and the two the KERNEL loads (hive-addon.opaque.codec, hive-addon.opaque.serve) are malli-free.
Mounting works for any IAddon. Passing hive-native's opacity audit asks two more things of the vendor, both measured on a real cljw build, and neither guessable from the audit's output:
READ RECORD FIELDS AS BARE SYMBOLS inside defrecord method bodies, not
through keyword accessors. A field read as (:state this) escapes as a
keyword, so the mangler must KEEP the field name; read as state it is
renamed. This is the one change that moved a real kernel's accounting
from :unprovable to :accounted.
Note the trap: (:field this) is the idiom hive-addon's own portable
stratum REQUIRES, because cljrs does not bind record fields in method
bodies. That rule is about cljrs. A vendor kernel is compiled by cljw
alone, which binds them, so the portable rule does not apply to it and
following it out of habit costs the certification.
NAME THINGS THE AUDITOR CAN PROBE. Below hive-native's extraction floor (6 characters) a name cannot be searched for, so its absence from a report is not evidence it is gone, and the audit reports :unprovable rather than passing. A short name is not safer, it is unaudited.
What :opaque does NOT claim: it means nothing the tool can REMOVE survived. A tuned numeric constant still reaches the constant pool and is reported at :medium under :numeric-literal, a class no elision pass can strip. Code on a customer's CPU is recoverable in the end; opacity is a cost imposed on an attacker, never a lock.
Facade for the OPAQUE addon seam: mount a compiled, source-free IAddon. The problem this solves is commercial, not technical. A proprietary addon cannot ship as a jar (every namespace name, every function name and the whole call graph read straight out of it) and cannot ship as source at all. It ships as ONE native binary with an embedded bytecode payload, built by `cljw build` from the vendor's own code written against this library's portable stratum, and the host mounts it through the generic proxy here. Nothing in the mount path is special-cased for it. An opaque addon is an ORDINARY mount manifest with :addon/type :external and :addon/trust-class :proprietary, so the existing licence gate (hive-addon.mount.entitlement) governs it, and an unlicensed addon's constructor namespace is never loaded. The vendor's side of the same wire is hive-addon.opaque.serve, and `entry-source` writes the entry that calls it, so the kernel and the proxy are framed by one library and cannot drift. JVM-only (.clj): it reaches the subprocess transport and writes manifest files. The strata below it are .cljc, and the two the KERNEL loads (hive-addon.opaque.codec, hive-addon.opaque.serve) are malli-free. ## Writing a kernel that can certify :opaque Mounting works for any IAddon. Passing hive-native's opacity audit asks two more things of the vendor, both measured on a real cljw build, and neither guessable from the audit's output: 1. READ RECORD FIELDS AS BARE SYMBOLS inside defrecord method bodies, not through keyword accessors. A field read as `(:state this)` escapes as a keyword, so the mangler must KEEP the field name; read as `state` it is renamed. This is the one change that moved a real kernel's accounting from :unprovable to :accounted. Note the trap: `(:field this)` is the idiom hive-addon's own portable stratum REQUIRES, because cljrs does not bind record fields in method bodies. That rule is about cljrs. A vendor kernel is compiled by cljw alone, which binds them, so the portable rule does not apply to it and following it out of habit costs the certification. 2. NAME THINGS THE AUDITOR CAN PROBE. Below hive-native's extraction floor (6 characters) a name cannot be searched for, so its absence from a report is not evidence it is gone, and the audit reports :unprovable rather than passing. A short name is not safer, it is unaudited. What :opaque does NOT claim: it means nothing the tool can REMOVE survived. A tuned numeric constant still reaches the constant pool and is reported at :medium under :numeric-literal, a class no elision pass can strip. Code on a customer's CPU is recoverable in the end; opacity is a cost imposed on an attacker, never a lock.
(->manifest spec)Derive a mount manifest (plain data) from an OpaqueSpec.
:addon/type is :external because the kernel is an out-of-process integration,
and :addon/trust-class is :proprietary so the licence gate governs it. The
spec rides as :addon/config, which addon-ctor reads back.
:addon/capabilities carries only what must be known before the kernel runs. The kernel's tools are NOT enumerated here: it self-describes, so a new build with new tools needs no new manifest.
Derive a mount manifest (plain data) from an OpaqueSpec. :addon/type is :external because the kernel is an out-of-process integration, and :addon/trust-class is :proprietary so the licence gate governs it. The spec rides as :addon/config, which `addon-ctor` reads back. :addon/capabilities carries only what must be known before the kernel runs. The kernel's tools are NOT enumerated here: it self-describes, so a new build with new tools needs no new manifest.
(addon-ctor config)The :addon/init-fn a mount manifest names. Reads the OpaqueSpec out of the manifest's :addon/config and answers the subprocess proxy.
One constructor for every opaque addon in the marketplace: the manifest differs, the code does not.
The :addon/init-fn a mount manifest names. Reads the OpaqueSpec out of the manifest's :addon/config and answers the subprocess proxy. One constructor for every opaque addon in the marketplace: the manifest differs, the code does not.
(build-argv spec)The command vector for one OpaqueSpec: the executable, then its declared args. A vendor whose kernel needs a licence file, a data directory or a feature flag passes them here, and no namespace between this one and the OS has to know what any of them mean.
The command vector for one OpaqueSpec: the executable, then its declared args. A vendor whose kernel needs a licence file, a data directory or a feature flag passes them here, and no namespace between this one and the OS has to know what any of them mean.
Project a live IAddon onto the wire, as the kernel does. Re-exported for a vendor checking what their addon will look like from the host side.
Project a live IAddon onto the wire, as the kernel does. Re-exported for a vendor checking what their addon will look like from the host side.
(emit! spec dir)Write the mount manifest for spec under classpath root dir at
META-INF/hive-addons/<addon-id>.edn, creating parents. Returns the path.
dir is supplied by the caller (a resources root), never a literal here.
Write the mount manifest for `spec` under classpath root `dir` at META-INF/hive-addons/<addon-id>.edn, creating parents. Returns the path. `dir` is supplied by the caller (a resources root), never a literal here.
(entry-source requires ctor-sym)(entry-source requires ctor-sym entry-ns)Emit the kernel entry namespace's source: a top-level call to
hive-addon.opaque.serve/serve! over the addon ctor-sym builds.
Two measured constraints are baked in and neither is obvious. A cljw build
artifact receives no command-line-args, so the entry cannot read flags; and
its main is never invoked, so the work must happen AT TOP LEVEL. An entry
that defines a tidy -main compiles cleanly and then does nothing at all.
requires is a vector of ns forms for the vendor's own namespaces, e.g.
[[acme.kernel :as k]], and ctor-sym is a fully qualified zero-argument
constructor returning an IAddon.
entry-ns is a parameter rather than a constant because there are two build
paths and they disagree about the name. A vendor calling cljw build
directly gets the default; hive-native's opacity pipeline stages the entry as
kernel-main and builds that symbol, so it passes its own. Hardcoding either
name makes this emitter unusable from the other path, which is how a second
copy of the entry shape gets written.
Emit the kernel entry namespace's source: a top-level call to hive-addon.opaque.serve/serve! over the addon `ctor-sym` builds. Two measured constraints are baked in and neither is obvious. A cljw `build` artifact receives no *command-line-args*, so the entry cannot read flags; and its main is never invoked, so the work must happen AT TOP LEVEL. An entry that defines a tidy -main compiles cleanly and then does nothing at all. `requires` is a vector of ns forms for the vendor's own namespaces, e.g. [[acme.kernel :as k]], and `ctor-sym` is a fully qualified zero-argument constructor returning an IAddon. `entry-ns` is a parameter rather than a constant because there are two build paths and they disagree about the name. A vendor calling `cljw build` directly gets the default; hive-native's opacity pipeline stages the entry as `kernel-main` and builds that symbol, so it passes its own. Hardcoding either name makes this emitter unusable from the other path, which is how a second copy of the entry shape gets written.
(inproc-addon spec served-addon)The proxy for a live IAddon over the in-process wire. Same proxy, same
framing, no subprocess: the difference between this and subprocess-addon is
exactly one transport, which is the point of the port.
The proxy for a live IAddon over the in-process wire. Same proxy, same framing, no subprocess: the difference between this and `subprocess-addon` is exactly one transport, which is the point of the port.
(line-transport line-fn)An ITransport from a raw (request-line -> response-line) function, for a test that must assert the exact BYTES on the wire.
An ITransport from a raw (request-line -> response-line) function, for a test that must assert the exact BYTES on the wire.
(manifest->edn manifest)Serialize a manifest to EDN text with the printer pinned, so keyword keys stay flat (:addon/id rather than #:addon{...}) and the bytes are a function of the value alone.
Serialize a manifest to EDN text with the printer pinned, so keyword keys
stay flat (:addon/id rather than #:addon{...}) and the bytes are a function
of the value alone.The closed wire vocabulary. Re-exported so a consumer that only requires this facade can still name an op.
The closed wire vocabulary. Re-exported so a consumer that only requires this facade can still name an op.
(serving-transport served-addon)An in-process ITransport that serves a LIVE IAddon through the real wire.
The addon is framed and answered by hive-addon.opaque.serve exactly as the compiled kernel would be, so this is a faithful transport rather than a stub. It is how a vendor proves an addon end to end BEFORE compiling it opaque, and how this library's own suite exercises the proxy without a binary.
An in-process ITransport that serves a LIVE IAddon through the real wire. The addon is framed and answered by hive-addon.opaque.serve exactly as the compiled kernel would be, so this is a faithful transport rather than a stub. It is how a vendor proves an addon end to end BEFORE compiling it opaque, and how this library's own suite exercises the proxy without a binary.
(subprocess-addon spec)The proxy for a kernel BINARY: an OpaqueAddon over a subprocess transport. This is what a marketplace artifact mounts through.
The proxy for a kernel BINARY: an OpaqueAddon over a subprocess transport. This is what a marketplace artifact mounts through.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |