# "Publishing Modules" _Path: en/guides/publishing_ > "Prepare, validate, publish, configure, and consume modules through the Wippy Hub." ## Table of Contents - Publishing Modules ## Content # Publishing Modules Publishing packages a module and makes a version or mutable label available through the Wippy Hub. This is a publishing workflow and reference. The `acme/*` modules, URLs, tokens, credentials, and example source are illustrative; replace them with resources owned by your organization. ## Prerequisites 1. Create an account on [hub.wippy.ai](https://hub.wippy.ai) 2. Create an organization or join one 3. Have permission to create modules in that organization — the first `wippy publish` registers the module automatically ## Module Structure ``` mymodule/ ├── wippy.yaml # Module manifest ├── src/ │ ├── _index.yaml # Entry definitions │ └── *.lua # Source files └── README.md # Documentation (optional) ``` ## wippy.yaml Define module metadata in `wippy.yaml`: ```yaml organization: acme module: http-utils type: library description: HTTP utilities and helpers license: MIT repository: https://github.com/acme/http-utils homepage: https://acme.dev keywords: - http - utilities authors: - Acme Engineering embed: - acme.http:assets exclude: - test/** - "*.test.lua" - acme.http:debug_handler exclude_meta: stage: - experimental metadata: support_url: https://acme.dev/support ``` | Field | Required | Description | |-------|----------|-------------| | `organization` | Yes | Organization name on the Hub | | `module` | Yes | Module name | | `type` | No | Module type: `library`, `application`, `agent`, or `plugin` | | `description` | No | Short description | | `license` | No | SPDX identifier (MIT, Apache-2.0) | | `repository` | No | Source repository URL | | `homepage` | No | Project homepage | | `keywords` | No | Search keywords | | `authors` | No | Author list | | `version` | No | Semantic version; `--version` overrides it | | `exclude` | No | Patterns to drop: values containing `:` are entry IDs, everything else is a source-file glob | | `embed` | No | Default `fs.directory` embed patterns when `--embed` is not passed | | `exclude_meta` | No | Metadata field to values map; entries whose metadata matches are dropped | | `metadata` | No | Arbitrary key/value metadata carried with the published module | | `publish.profiles` | No | Which config profiles to ship in the pack (see [Publishing Profiles](#publishing-profiles)) | | `publish.runtime` | No | Which runtime config sections to ship as pack defaults; `type: application` only | `exclude` splits by shape rather than by a separate field. `_old/**`, `test/**` and `*.test.lua` filter source files as they are collected; `acme.http:debug_handler` disables a registry entry after entries are decoded. A `**` segment spans any number of directory segments. `type` controls how the Hub classifies the module and can be changed in a later publish. The `--module-type` flag overrides it for one publish. When omitted, a newly created module defaults to `application` with a deprecation warning. ## Entry Definitions Define the module's entries in `_index.yaml`: ```yaml version: "1.0" namespace: acme.http entries: - name: definition kind: ns.definition meta: title: HTTP Utilities description: Helpers for HTTP operations readme: file://README.md wiki: GUIDE.md: file://docs/GUIDE.md examples/auth.md: file://docs/auth.md - name: client kind: library.lua source: file://client.lua modules: - http_client - json ``` The `wiki:` map on `ns.definition` publishes documentation pages alongside the README. Keys are page paths, and values are `file://` references. Contents are inlined during packing and served by the Hub as a module wiki. ## Dependencies Declare dependencies on other modules: ```yaml entries: - name: __dependency.wippy.test kind: ns.dependency meta: description: Testing framework component: wippy/test version: ">=0.3.0" ``` Version constraints: | Constraint | Meaning | |------------|---------| | `*` | Any version | | `1.0.0` | Exact version | | `>=1.0.0` | Minimum version | | `^1.0.0` | Compatible (same major) | ## Requirements Define configuration that consumers must provide: ```yaml entries: - name: api_endpoint kind: ns.requirement meta: description: API endpoint URL targets: - entry: acme.http:client path: ".meta.endpoint" default: "https://api.example.com" ``` Targets specify where the value is injected: - `entry` — Full entry ID to configure - `path` — Dot path into the target entry for value injection `default` accepts any scalar type — `default: 20` flows into a numeric target as a number, not a string. The same applies to `parameters[].value` on `ns.dependency` entries, and both accept `${env:NAME}` references, carried verbatim and resolved when the target entry is decoded. Consumers can configure the target through an override. The `-o` flag accepts a `namespace:entry:field=value` value: ```bash wippy run -o acme.http:client:meta.endpoint=https://custom.api.com ``` ## Imports Reference other entries: ```yaml - name: handler kind: function.lua source: file://handler.lua modules: - json imports: client: acme.http:client # Same namespace utils: acme.utils:helpers # Different namespace base_registry: :registry # Built-in ``` In Lua: ```lua local client = require("client") local utils = require("utils") ``` ## Contracts Define public interfaces: ```yaml - name: http_contract kind: contract.definition meta: name: HTTP Client Contract methods: - name: get description: Perform GET request - name: post description: Perform POST request - name: http_contract_binding kind: contract.binding contracts: - contract: acme.http:http_contract methods: get: acme.http:get_handler post: acme.http:post_handler ``` ### 1. Authenticate ```bash wippy auth login ``` ### 2. Prepare ```bash wippy init wippy update wippy lint ``` ### 3. Validate ```bash wippy publish --dry-run ``` Publish builds the pack the same way with or without `--dry-run`, so validation covers everything the real publish would produce: - `organization` and `module` must be lowercase alphanumeric with interior hyphens, `version` must be semver, and `type` must be one of the four module types. - `publish.runtime` is application-owned: declaring `source`, `sections`, or `vars` under it without `type: application` fails. - Every resource declaring `meta.artifact.format` is inspected by that format. A malformed artifact fails here rather than in a consumer, and two artifacts whose outputs would land in overlapping directories are rejected. - The `node-package` format additionally requires `package.json` to carry a semantic `version` that **equals the module version being published**, a valid package `name`, and no `preinstall`, `install`, `postinstall`, or `prepare` lifecycle script. The last rule is the one that bites during a release: bump `version` in `wippy.yaml` and in the artifact's `package.json` together, or the publish stops. ### 4. Publish ```bash wippy publish --version 1.0.0 ``` With release notes: ```bash wippy publish --version 1.0.0 --release-notes "Initial release" ``` ### Publish Flags | Flag | Description | |------|-------------| | `--label ` | Publish as a mutable label (e.g. `latest`, `beta`) instead of an immutable version | | `--protected` | Mark the published version as protected (cannot be deleted or overwritten) | | `--registry ` | Override the registry URL for this publish | | `--config ` | Directory containing `wippy.yaml` (default: current dir) | | `--create` | Register the module on the hub if it does not exist yet, then publish | | `--module-visibility ` | Visibility for `--create`: `private` (default) or `public` | | `--module-type ` | Module type: `library`, `application`, `agent`, or `plugin` (overrides `type:` in wippy.yaml) | | `--module-display-name ` | Display name for `--create` | ### Embed Static Files Modules with `fs.directory` entries (static assets, templates, public files) must use `--embed` to include them in the published package. Without it, an `fs.directory` entry is packed without its directory contents. ```bash wippy publish --version 1.0.0 --embed app:public_files wippy publish --version 1.0.0 --embed app:assets,app:templates ``` The manifest list and `--embed` flag accept entry IDs or names matching `fs.directory` entries. The same CLI flag is available on `wippy pack`; a CLI selection overrides the manifest list for that invocation. ### First Publish On its first publish, a module is registered on the Hub as private by default, and the publish retries once. Use `--create` to register it before publishing and set its properties: ```bash wippy publish --create --version 0.1.0 \ --module-visibility public \ --module-type library \ --module-display-name "HTTP Utils" ``` `--create` is idempotent — for an already-registered module the create step is a no-op. If your account cannot create modules in the organization, the hub returns a permission error instead of publishing. ### Publishing to a Local Hub Point `--registry` at a locally running Hub to publish and install without using the public registry. Plain HTTP is allowed only for local hosts: `localhost`, `127.0.0.1`, and the container aliases `host.docker.internal` (Docker Desktop or OrbStack) and `host.containers.internal` (Podman). Other hosts must use HTTPS. ```bash wippy auth login --registry http://localhost:8080 --token wpy_xxx wippy publish --registry http://localhost:8080 --create --version 0.1.0 ``` The registry and token can also come from the `WIPPY_REGISTRY` and `WIPPY_TOKEN` environment variables. When unset, the registry defaults to `https://hub.wippy.ai`. ### Quotas If the organization's private-module quota is exhausted, publishing fails with a message such as `cannot publish: Private-module quota exhausted (5 of 5)...`. Make the module public or ask an organization administrator to raise the quota. Uploads and downloads retry automatically after transient network errors. ## Publishing Runtime Defaults Applications with `type: application` can include runtime configuration defaults in their packs through `publish.runtime` in `wippy.yaml`: ```yaml type: application publish: runtime: source: .wippy.yaml # default: .wippy.yaml sections: [security, registry, override] vars: [public_url] ``` | Field | Description | |-------|-------------| | `source` | Config file the sections are read from (default: `.wippy.yaml`) | | `sections` | Runtime config sections copied into pack metadata as defaults | | `vars` | Explicit allowlist of variables to pack even when unreferenced | Rules: - Only variables referenced by the selected sections or published profiles are packed (followed transitively); everything else needs a `vars` entry. - `${env:...}` references in exported config are rejected — publisher environment never leaks into a pack. - The machine-local sections `boot`, `extensions`, and `workspace` cannot be exported. - Only the main application pack provides host runtime defaults; runtime metadata in dependency packs is ignored. At the destination, configuration precedence runs from application-pack defaults through runtime defaults, local configuration files, selected profiles, and finally CLI overrides. ## Publishing Profiles Root application profiles are exported into the pack's `runtime.profiles` metadata. Publishing does not select or bake a profile — consumers pick one at run time with `wippy run --profile `: ```yaml publish: profiles: enabled: true source: config/profiles.yaml # default: .wippy.yaml include: [production] # omit to publish all non-workspace profiles ``` `include: []` publishes none; an unknown name fails the publish. `workspace` sub-sections are never exported, even inside a published profile. See [Configuration](guides/configuration.md#profiles) for declaring profiles. ### Add Dependency ```bash wippy add acme/http-utils wippy add acme/http-utils@1.0.0 wippy install ``` ### Configure Requirements Override values at runtime: ```bash wippy run -o acme.http:client:meta.endpoint=https://my.api.com ``` Or in `.wippy.yaml`: ```yaml override: acme.http:client:meta.endpoint: "https://my.api.com" ``` ### Import in Your Code ```yaml # your src/_index.yaml entries: - name: __dependency.acme.http kind: ns.dependency component: acme/http-utils version: ">=1.0.0" - name: my_handler kind: function.lua source: file://handler.lua imports: http: acme.http:client ``` ## Example Module **wippy.yaml:** ```yaml organization: acme module: cache type: library description: In-memory caching with TTL license: MIT keywords: - cache - memory ``` **src/_index.yaml:** ```yaml version: "1.0" namespace: acme.cache entries: - name: definition kind: ns.definition meta: title: Cache Module - name: cache kind: library.lua source: file://cache.lua modules: - time ``` **src/cache.lua:** ```lua local time = require("time") local cache = {} local store = {} function cache.set(key, value, ttl) store[key] = { value = value, expires = ttl and (time.now():unix() + ttl) or nil } end function cache.get(key) local entry = store[key] if not entry then return nil end if entry.expires and time.now():unix() > entry.expires then store[key] = nil return nil end return entry.value end return cache ``` Publish: ```bash wippy init wippy update wippy lint wippy publish --version 1.0.0 ``` ## See Also - [CLI Reference](guides/cli.md) — Publishing commands and flags - [Entry Kinds](guides/entry-kinds.md) — Module and dependency entries - [Configuration](guides/configuration.md) — Runtime configuration and profiles ## Navigation Previous: "Supervision" (guides/supervision) Next: "Compute Units" (concepts/compute-units)