Package Manager
tulpar pkg is the built-in package manager for Tulpar. It manages
project dependencies via a small TOML manifest (tulpar.toml) and
writes a deterministic lockfile (tulpar.lock) on every install so
your tulpar_modules/ tree is reproducible across machines.
The official package registry lives at
pkg.tulparlang.dev — point
[registry] url there in your manifest to install community packages.
Quick start
Section titled “Quick start”# Inside an empty project directorytulpar pkg init my-api
# Add a local-path dependency (development)tulpar pkg add greeter@path:../greeter
# Add a single-file URL dependencytulpar pkg add lodash@url:http://my-cdn/lodash.tpr
# Add a semver-style registry dependency (requires [registry] url in tulpar.toml)tulpar pkg add wings@1.2.3
# Vendor everything into ./tulpar_modulestulpar pkg installAfter tulpar pkg install, the consumer can import "greeter" and the
runtime resolves it from tulpar_modules/greeter/greeter.tpr.
Manifest format
Section titled “Manifest format”tulpar.toml is a deliberately tiny TOML subset — string values only,
top-level keys plus [registry] and [dependencies] tables.
name = "my-api"version = "0.1.0"description = "Tulpar HTTP API example"author = "Hamza"license = "MIT"
[registry]url = "https://api.pkg.tulparlang.dev" # the API base; this is also the default
[dependencies]wings = "1.2.3" # registrygreeter = "path:../greeter" # local sibling dirlodash = "url:http://cdn/lodash.tpr" # single-file URLVersion specs
Section titled “Version specs”| Form | Meaning |
|---|---|
path:./local/dir | Recursively copy *.tpr from a local directory. |
url:http://example.com/x.tpr | Plain HTTP fetch of a single .tpr file. |
1.2.3 | Exact version from the registry. |
^1.2.3 / ~1.2.3 | Semver range — caret (within major) / tilde (within minor). |
>=1.0,<2.0 / * / latest | Comparator/compound range, or highest published version. |
For an exact version Tulpar hits the registry directly. For a range
(^, ~, a </>/= comparator, a comma-compound, or */latest) it
first fetches the package’s published versions from
<registry>/v1/packages/<name>, picks the highest one that satisfies the
range, then downloads
<registry>/v1/packages/<name>/versions/<version>/source.
A registry version spec requires a registry URL — [registry] url = "..."
in the manifest, the --registry flag, or the TULPAR_REGISTRY env var
(default https://api.pkg.tulparlang.dev). https:// URLs require Tulpar to
be built with OpenSSL (pacman -S mingw-w64-x86_64-openssl on MSYS2).
Lockfile
Section titled “Lockfile”After every successful pkg install, Tulpar writes tulpar.lock next
to your manifest:
# tulpar.lock — auto-generated by `tulpar pkg install`.# DO NOT EDIT. Commit alongside tulpar.toml so re-installs are reproducible.
[resolved]wings = "https://api.pkg.tulparlang.dev/v1/packages/wings/versions/1.2.3/source"greeter = "path:../greeter"lodash = "url:http://cdn/lodash.tpr"The lockfile records the fully resolved URL or path plus a SHA-256
of the downloaded bytes for each dependency, so a re-install on another
machine fetches the exact same bytes — even if the registry’s latest
pointer moves. A re-install that finds the same URL serving a different
SHA-256 than the lockfile records is refused rather than silently
overwritten.
Module resolution
Section titled “Module resolution”When the AOT compiler sees import "name", it tries (in order):
- The embedded standard library (
wings,router,http_client,orm,test, …). - A bundle-local sibling —
<importing-file's-dir>/name.tpr— so a multi-file package’s internal imports resolve to its own files. - The literal path
./name. - The literal path with
.tprappended (./name.tpr). ./tulpar_modules/<name>/<name>.tpr— the vendored entry-point convention../tulpar_modules/<name>.tpr— single-file vendor.
That ordering means the embedded stdlib names (wings, orm, …) always win
— you can’t shadow wings with a local wings.tpr — and a vendored package
in tulpar_modules/ is the fallback for everything else.
Subcommands
Section titled “Subcommands”| Command | Effect |
|---|---|
tulpar pkg init [name] | Create a starter tulpar.toml (refuses to overwrite). |
tulpar pkg list | Print package metadata + dependencies. |
tulpar pkg add <name>[@<ver-spec>] | Add or update a manifest dependency line. |
tulpar pkg remove <name> | Drop a manifest dependency line. |
tulpar pkg install | Vendor every dep into tulpar_modules/ + write tulpar.lock. |
tulpar pkg search <query> | Search the registry catalog by name/description. |
tulpar pkg info <name> | Print a package’s registry metadata + published versions. |
tulpar pkg publish [--dry-run] | Bundle the project’s sources and POST them to the registry. |
Publishing
Section titled “Publishing”Packages on pkg.tulparlang.dev are published from public GitHub
repositories — the registry fetches and caches your source at a ref you pin,
so what installs is immutable and independent of GitHub’s uptime. There are two
ways to publish:
- From a GitHub repo (recommended, self-serve) — for anyone with an approved account. You point the registry at your public repo + ref and it fetches the source.
- Bundle upload via the CLI —
tulpar pkg publish, owner/maintainer-level (bearer token). Uploads your project’s.tprsources directly.
Either way, every new package version is reviewed by the registry owner before it appears in the public catalog.
1. Create an account
Section titled “1. Create an account”Go to pkg.tulparlang.dev and either:
- Sign in with GitHub (recommended) — links your GitHub identity, which is required to publish a repo; or
- Register with email + password.
New accounts start in a pending state.
2. Get approved
Section titled “2. Get approved”The registry owner reviews new accounts and approves or rejects them. You can sign in and see your status on your account page, but you can’t publish until you’re approved — this keeps the catalog curated.
3. Prepare your repo
Section titled “3. Prepare your repo”Your package lives in a public GitHub repo you own. At the ref you publish (a tag, branch, or commit), the repo must contain the package’s entry file:
- By default,
<name>.tprat the repo root (e.g.wings_jwt.tpr), or - any path you pass as
entry(e.g.src/mypkg.tpr).
Tag a release so the ref is stable:
git tag v1.0.0 && git push origin v1.0.04. Publish from the web
Section titled “4. Publish from the web”Sign in, then open pkg.tulparlang.dev/publish and fill in:
| Field | Example | Notes |
|---|---|---|
| Package name | my_pkg | The name users tulpar pkg add. |
| Version | 1.0.0 | Semver; immutable once published. |
| GitHub repo | you/my_pkg | owner/name; must be under your GitHub login. |
| Ref | v1.0.0 | Tag, branch, or commit SHA. |
| Entry file | my_pkg.tpr | Optional; defaults to <name>.tpr. |
| Description | One-line summary | Optional. |
On submit, the registry fetches
https://raw.githubusercontent.com/<repo>/<ref>/<entry>, caches the bytes, and
records the version as pending owner approval.
Publish via the API
Section titled “Publish via the API”The web form posts to the registry’s HTTP API; you can call it directly with your session token (or the owner can use the publish token):
POST https://api.pkg.tulparlang.dev/v1/publish/githubAuthorization: Bearer <session-or-publish-token>Content-Type: application/json
{ "name": "my_pkg", "version": "1.0.0", "repo": "you/my_pkg", "ref": "v1.0.0", "entry": "my_pkg.tpr", "description": "One-line summary"}entry and description are optional. Ownership: a non-owner account may
only publish repos whose owner matches its linked GitHub login
(case-insensitive); the owner (or a holder of the registry’s publish token) may
publish any repo.
5. Approval → public
Section titled “5. Approval → public”A freshly published version is hidden from the public catalog (and 404s on
direct fetch) until the owner approves it. Once approved it shows up in
tulpar pkg search and installs with tulpar pkg add. Versions are
immutable — a published (name, version) is pinned forever; re-publishing
the same pair returns 409 Conflict.
CLI publishing (bundle upload)
Section titled “CLI publishing (bundle upload)”The owner/maintainer path bundles every .tpr in your project and uploads the
bytes directly — no GitHub repo needed. Auth is a bearer token (--token <tok>
or TULPAR_PUBLISH_TOKEN):
export TULPAR_PUBLISH_TOKEN="…"tulpar pkg publish --dry-run # bundle + show what would ship, no uploadtulpar pkg publish # POST to <registry>/v1/publishThe registry target follows the same resolution as installs (--registry flag
→ [registry] url → TULPAR_REGISTRY → the default
https://api.pkg.tulparlang.dev). --dry-run skips the POST so you can inspect
the bundle without a token.
Errors
Section titled “Errors”| Status | Meaning |
|---|---|
401 authentication required | Not signed in (and no publish token). |
403 account not approved | Your account is still pending or was rejected. |
403 github_required | Publishing a repo needs a linked GitHub login — sign in with GitHub. |
403 not_your_repo | The repo isn’t under your GitHub login. |
409 conflict | (name, version) already published — versions are immutable. |
502 fetch_failed | The registry couldn’t fetch the source — check the repo/ref/entry are public and correct. |
Roadmap
Section titled “Roadmap”- Private-repo publishing (token-scoped fetch).
- Multi-file packages from a repo (tarball extract).
- Package signing (the lockfile already records a SHA-256 per dependency).