The Synoema package system lets you share reusable Synoema code with others — and install code others have shared. In this tutorial we'll build a small but real package: sno-units, a unit-conversion library for physical measurements (temperature, pressure, distance).

By the end you'll know how to:

Prerequisites

Install Synoema if you haven't already:

curl -fsSL https://synoema.tech/install.sh | sh
sno --version   # should print 0.1.0-beta.1 or newer

Step 1 — Create the directory

mkdir sno-units
cd sno-units

A Synoema package is just a directory. No build system, no generated files.

Step 2 — Write the manifest (sno.toml)

Create sno.toml:

[package]
name        = "sno-units"
version     = "0.1.0"
description = "Physical unit conversions: temperature, pressure, distance"
keywords    = ["units", "conversion", "temperature", "physics", "iot"]
license     = "MIT"
author      = "Your Name <you@example.com>"
min_lang_version = "0.1.0-beta.1"

[package.exports]
functions = [
  "celsius_to_fahrenheit",
  "fahrenheit_to_celsius",
  "celsius_to_kelvin",
  "hpa_to_psi",
  "psi_to_hpa",
  "meters_to_feet",
  "feet_to_meters",
  "km_to_miles",
  "miles_to_km",
]

[package.use_cases]
examples = [
  "Convert sensor temperature readings from Celsius to Fahrenheit",
  "Display pressure readings in PSI or hPa depending on locale",
  "Convert GPS altitude from meters to feet for aviation use",
  "Unit conversion in IoT sensor data pipelines"
]

[package.capabilities]
network  = false
fs_read  = false
fs_write = false
exec     = false

A few things to note:

Step 3 — Write the library (lib.sno)

Create lib.sno:

-- sno-units: physical unit conversion library

-- ── Temperature ──────────────────────────────────────────────────────────────

celsius_to_fahrenheit c = c * 1.8 + 32.0

fahrenheit_to_celsius f = (f - 32.0) / 1.8

celsius_to_kelvin c = c + 273.15

kelvin_to_celsius k = k - 273.15

-- ── Pressure ─────────────────────────────────────────────────────────────────

-- hectopascals → pounds per square inch
hpa_to_psi hpa = hpa * 0.0145038

-- pounds per square inch → hectopascals
psi_to_hpa psi = psi * 68.9476

-- ── Distance ─────────────────────────────────────────────────────────────────

meters_to_feet m = m * 3.28084

feet_to_meters ft = ft / 3.28084

km_to_miles km = km * 0.621371

miles_to_km mi = mi / 0.621371

This is pure Synoema — no imports, no side effects. Every function takes a number and returns a number. The Hindley-Milner type checker will infer all types automatically.

Step 4 — Type-check

sno check lib.sno

Expected output:

✓ lib.sno — OK (11 bindings, 0 warnings)

If you see a type error, fix it before continuing. sno check is fast (under 50ms for this file) and can be run on every save.

Step 5 — Test locally

Create a quick test script:

-- test.sno
import "./lib.sno"   -- relative path, not registry

main =
  p1 = print ("0°C = " ++ show (celsius_to_fahrenheit 0.0) ++ "°F")
  p2 = print ("100°C = " ++ show (celsius_to_fahrenheit 100.0) ++ "°F")
  p3 = print ("1013 hPa = " ++ show (hpa_to_psi 1013.25) ++ " PSI")
  p4 = print ("5 km = " ++ show (km_to_miles 5.0) ++ " mi")
sno run test.sno
0°C = 32.0°F
100°C = 212.0°F
1013 hPa = 14.695... PSI
5 km = 3.10685... mi

The import path "./lib.sno" is a file-system import. When users install your package via sno pkg add and write import "sno-units", the runtime resolves it to the installed package directory instead.

Step 6 — Add an example

Good packages include examples. Create examples/demo.sno:

-- Example: sno-units in a sensor reading pipeline
import "sno-units"

-- Simulate a sensor callback
process_sensor_reading celsius_raw hpa_raw =
  f = celsius_to_fahrenheit celsius_raw
  psi = hpa_to_psi hpa_raw
  ("Temp: " ++ show f ++ "°F  |  Pressure: " ++ show psi ++ " PSI")

main =
  -- Typical values from a BME280 sensor
  print (process_sensor_reading 23.4 1008.7)
  print (process_sensor_reading (-5.0) 1020.0)
sno run examples/demo.sno

Step 7 — Publish

7.1 Sign in

sno pkg login

This opens your browser for GitHub or Google OAuth. On success:

✓ Logged in as your-username

For CI/CD, create an API key from your profile page and use:

sno pkg login --key sno_your_api_key

7.2 Run sno publish

sno publish

The CLI will:

  1. Validate sno.toml (all required fields present)
  2. Run sno check lib.sno (type-check)
  3. Scan for unsafe content (no binaries in a source-only registry)
  4. Check for breaking changes vs. previous version (none for 0.1.0)
  5. Upload the tarball
Validating sno.toml         ✓
Type-checking lib.sno       ✓ (11 bindings)
Content scan                ✓ (source only)
Breaking-change diff        ✓ (first release)
Uploading sno-units@0.1.0  ✓

Published: https://synoema.tech/packages/sno-units

Step 8 — Verify the published package

sno pkg search units
sno pkg add sno-units   # installs from registry

Create a new file outside your package directory:

-- anywhere/test_installed.sno
import "sno-units"

main =
  print (celsius_to_fahrenheit 37.0)   -- body temperature
sno run test_installed.sno
# → 98.6

Your package is live and installable by anyone.

Updating your package

Edit lib.sno (add a function, fix a bug), bump the version in sno.toml, and publish again:

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

The registry keeps all versions. Users on sno pkg add sno-units@^0.1 will get 0.1.1 on their next sno pkg add. Users who pinned to @0.1.0 keep 0.1.0.

What makes a good package

Full package layout (reference)

sno-units/
  sno.toml          # manifest — required
  lib.sno           # library entry point — required
  README.md         # shown on the package page in the registry
  examples/
    demo.sno        # runnable example

Next steps