Packages Guide

Everything you need to know about the Synoema package ecosystem — from installing your first package to publishing your own.

1. Using packages

The Synoema package manager is built into the CLI. One command installs a package and pins it to your project.

1.1 Finding packages

Browse the packages catalog on this site, or search from the CLI:

sno pkg search http
sno pkg search "time series"
sno pkg search --json iot        # machine-readable output

1.2 Installing a package

# Install latest version
sno pkg add sno-http

# Install a specific version
sno pkg add sno-http@0.2.0

# Install from a local path (development)
sno pkg add ./my-local-lib

# Install from a URL
sno pkg add https://example.com/my-pkg.tar.gz

Packages are stored in ~/.sno/packages/<name>@<version>/. No system-wide changes — fully sandboxed.

1.3 Importing a package in your code

Use the import keyword at the top of your .sno file:

import "sno-http"

-- Use exported functions directly
main =
  http_serve 3000 (\method path ->
    http_html "<h1>Hello</h1>")

Synoema resolves the import to ~/.sno/packages/sno-http@latest/lib.sno automatically. If multiple versions are installed, it picks the highest compatible one.

1.4 Listing installed packages

sno pkg list          # human-readable table
sno pkg list --json   # machine-readable, includes compat status

1.5 Removing a package

sno pkg remove sno-http
Tip: Packages are resolved at startup, not at import time. If you add a package while a long-running server is running, restart it.

2. Creating a package

A Synoema package is a directory containing at minimum two files: sno.toml (manifest) and lib.sno (library code).

2.1 Directory layout

my-pkg/
  sno.toml       # manifest — required
  lib.sno        # library entry point — required
  README.md      # optional but recommended
  examples/      # optional example programs
    demo.sno

2.2 sno.toml manifest

The manifest describes your package for the registry and the package manager.

[package]
name        = "my-pkg"
version     = "0.1.0"
description = "A short description (shown in search results)"
keywords    = ["iot", "sensors", "data"]
license     = "MIT"
author      = "Your Name <you@example.com>"
min_lang_version = "0.1.0-beta.1"

# Which functions/values this package exports
[package.exports]
functions = ["process_data", "format_result", "DEFAULT_TIMEOUT"]

# How LLM agents should use this package
[package.use_cases]
examples = [
  "Process raw sensor readings into calibrated values",
  "Format telemetry data for MQTT publishing"
]

# Runtime capabilities (used for safety checks in --untrusted mode)
[package.capabilities]
network  = false
fs_read  = false
fs_write = false
exec     = false

2.3 lib.sno — library entry point

Write your library functions in lib.sno. Everything at the top level is exported (subject to [package.exports] filtering in the registry).

-- my-pkg/lib.sno

DEFAULT_TIMEOUT = 5000

-- Process a raw integer reading into a calibrated float
process_data raw calibration_factor =
  (to_float raw) * calibration_factor

-- Format a value for display
format_result label value unit =
  label ++ ": " ++ show value ++ " " ++ unit

Keep lib.sno pure when possible — avoid side effects at the top level. Side effects (network, file I/O) should be inside functions, not executed on import.

2.4 Type-checking your package

cd my-pkg/
sno check lib.sno      # parse + type-check without running
sno fmt lib.sno        # auto-format in-place

2.5 Testing locally

Create an example program that imports your package by local path:

-- examples/demo.sno
import "./lib.sno"    -- relative path import

main =
  result = process_data 1024 0.0488
  print (format_result "Temperature" result "°C")
sno run examples/demo.sno

2.6 Using a package from another package

Install the dependency, then import it in lib.sno:

sno pkg add sno-http
-- lib.sno
import "sno-http"

my_server port =
  http_serve port (\m p -> http_html "<h1>OK</h1>")
Note: Transitive dependencies are resolved automatically. The package manager walks the dependency tree up to 16 levels deep.

3. Publishing to the registry

3.1 Sign in

Publishing requires a Synoema account (GitHub or Google OAuth):

sno pkg login

This opens your browser for OAuth and saves a session token to ~/.sno/config.toml. For CI/CD environments, use an API key instead:

# Create an API key from your profile page
# https://synoema.tech/profile

sno pkg login --key sno_your_api_key_here

3.2 Prepare your package

Before publishing, verify everything is correct:

cd my-pkg/

# 1. Format code
sno fmt lib.sno

# 2. Type-check
sno check lib.sno

# 3. Verify sno.toml is valid and complete
#    (name, version, description, keywords, license are required)
cat sno.toml

3.3 Publish

sno publish

This command (run from the package directory):

  1. Validates sno.toml for required fields
  2. Runs sno check on lib.sno (type-checks source)
  3. Scans for unsafe binary content
  4. Detects breaking changes vs. the previous published version (type + contract diff)
  5. Uploads the source archive to the registry

On success, the package is immediately available at synoema.tech/packages.

3.4 Breaking-change detection

The registry compares the type signatures and contracts of all exported functions between versions. If a breaking change is detected (removed function, changed type, stricter contract), you'll see a warning:

⚠ Breaking change detected:
  process_data: was (Int -> Float -> Float), now (Int -> Int -> Float -> Float)
  (added parameter — callers must be updated)

Bump major version? [y/N]

3.5 Yanking a version

If you publish a broken version, yank it to prevent new installs (existing installs are not affected):

sno publish --yank 0.1.1   # yank specific version

3.6 Re-publishing an existing version

Not allowed. Each version is immutable. Bump the patch version instead:

# Edit sno.toml: version = "0.1.1"
sno publish

4. Versioning & semver

Synoema packages use semantic versioning (semver): MAJOR.MINOR.PATCH[-pre].

Change type Bump Example
Bug fix, no API change PATCH 0.1.0 → 0.1.1
New function, backward-compatible MINOR 0.1.1 → 0.2.0
Removed function, changed signature MAJOR 0.2.0 → 1.0.0
Pre-release / beta 1.0.0-beta.1

Version ranges in sno pkg add

sno pkg add my-pkg@^1.0   # compatible with 1.x.x (default)
sno pkg add my-pkg@~0.2   # compatible with 0.2.x
sno pkg add my-pkg@1.2.3  # exact version

5. Capabilities & safety metadata

The [package.capabilities] section in sno.toml declares what system resources your package accesses. This metadata is shown in the registry and enforced in --untrusted mode.

[package.capabilities]
network  = false   # no outbound HTTP/TCP
fs_read  = false   # no file system reads
fs_write = false   # no file system writes
exec     = false   # no subprocess execution

If your package uses http_get or tcp_connect, declare network = true. If it calls file_read, declare fs_read = true.

Being honest about capabilities builds trust with users. The registry displays a capabilities badge on each package page:

✓ no-network ✓ no-fs ✓ no-exec ⚠ network

LLM use_cases metadata

The [package.use_cases] section helps MCP-connected LLM agents discover your package for relevant tasks:

[package.use_cases]
examples = [
  "Parse NMEA GPS sentences from serial port",
  "Convert WGS-84 coordinates to local grid",
  "Compute distance between two GPS waypoints"
]

When a developer asks their LLM “how do I work with GPS data?”, the MCP server will surface packages whose use_cases match the query.

Quick reference

Using packages

sno pkg search <query>
sno pkg add <name[@ver]>
sno pkg add ./local-path
sno pkg list
sno pkg remove <name>

Publishing

sno pkg login
sno pkg login --key <key>
sno check lib.sno
sno publish
sno publish --yank <ver>

Next steps

Browse packages →

Explore the catalog of published packages

Publish a package →

Step-by-step checklist for first-time publishers

Tutorial: first package →

Build a real package from scratch in 10 minutes

My Profile →

Manage your packages and API keys