Publishing a package

Create an account, mint a publish token, then push a versioned package to the Sere registry from your library project with sere login and sere publish.

The registry is part of this site. You do not need a database account, a third-party login, or a key copied out of a console — you sign up here, create a token, and start uploading.

1. Create a developer account

Open sere-lang.com/developers/signup and fill in three fields:

FieldRules
EmailAny address you can receive mail at. Used for sign-in and confirmation.
Handle3–40 characters. Lowercase letters, digits and dashes; must start and end with a letter or digit. Unique across the registry, and shown next to the packages you publish.
PasswordAt least 10 characters, at most 128. It must not contain the part of your email before the @.

Submit the form and one of two things happens:

  • Instant access. You land on your dashboard. Nothing else to do.
  • Confirm your email first. We send a confirmation link. Open it and the account activates; the link returns you to the dashboard already signed in.

If it does not arrive within a couple of minutes, check the spam folder first. Sign-ups are rate limited to 5 per hour per network, and resends to 3 per hour.

If the confirmation email never arrives

Press Didn't get the confirmation email? Resend it on the sign-up form. That is rate limited, and the answer is the same whether or not the address is registered.

If resending does not help, it is almost always one of these:

CauseHow to tellWhat to do
Sign-in keeps saying the password is wrong, and no email arrivesThe account exists but was never confirmed, and the deployment cannot send email.The site's operator should set DEVELOPER_AUTO_CONFIRM=true, after which signing in again activates the account; or configure a mail sender.
The address already has a confirmed accountThe form says a new link is on the way, but nothing is ever sent. The audit trail records developer.signup_existing with confirmationEmailSent: false.Sign in instead, or sign up with a different address.
The deployment has no working mail senderEvery sign-up fails with "we could not send the confirmation email".The site's operator needs to add mail credentials and check the sender's logs.
The provider's shared test sender is throttledMail works sometimes, then stops.The site's operator needs a real mail sender: test senders are capped at a couple of messages per hour.
The deployment restricts which addresses may sign upSign-up fails with an address error.Use an address the deployment allows.

Email confirmation is a deployment setting. Where it is switched off, a new account is signed in immediately and no email is involved at all — both settings work with this flow.

Already have an account? Sign in. Sessions last as long as you keep using the site: the access cookie is refreshed in the background, and the refresh cookie lasts 30 days.

2. Create a publish token

A token is a password for your tooling. It is how the API knows a request is really you, without sharing your account password.

  1. Go to sere-lang.com/developers.
  2. Under Publish tokens, give the token a label that says where it lives — laptop, github-actions, release-bot. Labels are for you; they have no effect on permissions.
  3. Choose an expiry: no expiry, 30 days, 90 days or 1 year. Per-machine tokens with an expiry are the safer default.
  4. Press Create token and copy the value immediately.
text
sere_4f0c9a21_9tK3xQpR7vLm2YbW8sN6dH1jC5zA0eU4rT7iG3oP
└──┬──┘ └──┬───┘ └─────────────────┬──────────────────┘
   │       │                       └── secret: shown once, stored only as a hash
   │       └── public prefix: what you see in the dashboard afterwards
   └── scheme

The full token is shown exactly once. The server keeps only a SHA-256 hash and the public prefix, so nobody — including an operator reading the database — can recover it. If you lose it, revoke that token and create another.

Hand the token to the CLI from inside your library project:

bash
cd hello-utils
sere login sere_4f0c9a21_9tK3xQpR7vLm2YbW8sN6dH1jC5zA0eU4rT7iG3oP

sere login stores the token and checks it against the registry immediately, so a value that is wrong, revoked or expired is refused there rather than halfway through an upload.

In CI, read the token from a secret rather than pasting it into a file you might commit:

bash
export SERE_TOKEN="sere_4f0c9a21_9tK3xQpR7vLm2YbW8sN6dH1jC5zA0eU4rT7iG3oP"

Revoke a token from the same dashboard at any time. Revoking takes effect on the next request, and leaves your other tokens alone.

3. Describe the package

The registry reads a manifest: sere.toml (or sere.json) in the root of your library. Only the keys below are used; anything else is ignored, so you can keep your own tooling metadata in the same file.

toml
[package]
name = "hello-utils"
version = "0.1.0"
summary = "Small helpers for Sere projects."
description = """
A longer explanation, shown on the package page.
"""
license = "MIT"
repository = "https://github.com/you/hello-utils"
homepage = "https://example.com/hello-utils"
author = "Your Name"
keywords = ["utilities", "strings"]
entry = "lib.sere"
KeyRequiredNotes
nameYesLowercase letters, digits, dots, dashes and underscores. Max 64 characters. Must start with a letter or digit.
versionYesSemantic version: MAJOR.MINOR.PATCH, optionally -prerelease. A leading v is stripped, so v1.2.0 is stored as 1.2.0.
summaryNoOne line, up to 280 characters. Defaults to the first line of description.
descriptionNoUp to 4000 characters.
licenseNoShown as a badge on the package page, e.g. MIT, Apache-2.0.
repository / homepageNoURLs, up to 300 characters each. Linked from the package page.
authorNoUp to 120 characters.
keywordsNoUp to 12 entries, lowercased and de-duplicated. They feed registry search.
entryNoPath to the library entry file. Defaults to lib.sere if omitted.

Add a README.md next to the manifest. It is rendered on your package page under README, so it is the best place to explain the API you are publishing.

4. Publish

Publishing happens from inside the library project — the folder that holds sere.toml. Sign the CLI in with your token, then publish:

bash
cd hello-utils
sere login sere_4f0c9a21_9tK3xQpR7vLm2YbW8sN6dH1jC5zA0eU4rT7iG3oP
sere publish

sere publish reads sere.toml, packs the library — the entry file plus the local modules it imports, or a .tar.gz / .zip of the library folder — and uploads the version the manifest names, with README.md alongside it. What you described in step 3 is what the registry stores, so a change ships by bumping version in sere.toml and publishing again.

A successful publish answers 201:

json
{
  "published": {
    "name": "hello-utils",
    "version": "0.1.0",
    "summary": "Small helpers for Sere projects.",
    "latestVersion": "0.1.0",
    "versionsCount": 1,
    "created": true,
    "bytes": 18421,
    "checksumSha256": "9f2c4d...e1",
    "tarballPath": "hello-utils/0.1.0/hello-utils-0.1.0.tar.gz",
    "downloadUrl": "https://…/storage/v1/object/public/packages/hello-utils/0.1.0/hello-utils-0.1.0.tar.gz",
    "url": "/api/packages/hello-utils",
    "install": "sere add hello-utils@0.1.0"
  }
}

Your package appears on Libraries immediately, and at /libraries/hello-utils.

Versions are immutable

Publishing a version that already exists is rejected with 409 — a version always resolves to the same bytes and the same checksum. To ship a change, bump the version. latest follows the highest semantic version in the registry, not the most recently uploaded one, so publishing 0.9.9 after 1.0.0 will not drag latest backwards.

A name belongs to one developer

The first publisher to claim a name owns it, and that never changes behind your back:

SituationResult
You publish a version under a name you already own200 — the version is added and the package's summary, description, links, author and keywords are refreshed from the new manifest. This is how you update a package.
You publish under a free name201 — the package is created and you own it.
You publish under a name another developer owns403 — refused, and nothing is written. Not even the tarball is kept.
A name first released with the deployment's own CI token403 — those names are reserved, so nobody can claim one just by asking first.

Names are never renamed or transferred. If you need a different name, publish under it; if you think a name has been abandoned, ask its owner to hand it over.

5. Install a published package

Once a version is on the registry, anyone can install it — downloads are public, so no account or token is involved. The short version:

bash
sere add hello-utils
sere add hello-utils@0.1.0

Or fetch the archive and drop it into libs/, where the compiler treats a folder or a .slib as an importable library:

bash
mkdir -p libs/hello-utils
curl -Lo hello-utils-0.1.0.tar.gz \
  "https://sere-lang.com/api/packages/hello-utils/0.1.0/download?stream=1"
tar -xzf hello-utils-0.1.0.tar.gz -C libs/hello-utils

Installing packages covers version selectors, checksum verification and the rest of the download API.

6. Publish from CI

A token plus two CLI calls is the whole integration. Guard the token with a secret, and publish on tag pushes:

yaml
name: publish
on:
  push:
    tags: ["v*"]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Sign the CLI in
        env:
          SERE_TOKEN: ${{ secrets.SERE_TOKEN }}
        run: sere login "$SERE_TOKEN"

      - name: Publish to the Sere registry
        run: sere publish

Both commands exit non-zero when the registry refuses them, so a rejected publish fails the job and prints the reason. If the version should come from the tag rather than from the manifest, write it into sere.toml before publishing.

7. What the API checks

StatusMeaningWhat to do
400The manifest, name or version is invalid, or a field is missing.The response names the field. Names are lowercase; versions need three numeric parts.
401No token, or the token is malformed, revoked or expired.Create a new token on the dashboard.
403Either the token cannot publish, or the package name belongs to someone else.Use a publish-scoped token; if the name is taken, publish under a different one.
409That version already exists.Bump the version.
413The archive is over 25 MB, or the README is over 64 KB.Trim the payload — the registry is for libraries, not build output.
415The request was not multipart/form-data.Only relevant to a hand-rolled client — sere publish always sends the right shape.
429Rate limited.The response carries Retry-After and ratelimit-reset. Back off and retry.
503Publishing or token checking is unavailable on the deployment.Nothing to fix in your request. Retry later — if you run the deployment, GET /api/registry/status with a publish token reports the cause.

If the token is refused

Publishing answers with a specific reason, so start by reading it:

MessageMeaning
"That is a token's public prefix, not the token"You copied the value from the token table. The table lists sere_xxxxxxxx…, which is only the public half. Create a new token and copy the whole value the moment it is shown.
"That does not look like a registry token"The stored value is not a token at all — often a Supabase key or an old credential. Run sere login again with a token from /developers.
"That publish token is not valid"The token is well-formed but the registry has no match for it. It may belong to a different deployment, or it was created before the registry schema existed. Create a new one on the deployment you are publishing to.
"That token has been revoked" / "has expired"Expected: the credential was retired. Create a replacement.
503 with "could not check that token"The deployment cannot reach its token table. The message names the cause — missing tables, a rejected key, or an unreachable database. GET /api/registry/status probes each dependency separately.

sere login <token> runs exactly this check. To do it by hand without publishing anything:

bash
curl -s -X POST https://sere-lang.com/api/developers/tokens/verify \
  -H "Authorization: Bearer $SERE_TOKEN"
powershell
Invoke-RestMethod -Method Post `
  -Uri https://sere-lang.com/api/developers/tokens/verify `
  -Headers @{ Authorization = "Bearer $env:SERE_TOKEN" }

A valid token answers 200 with "valid": true, its scope and the deployment status. Anything else tells you exactly which of the cases above you are in.

Limits

LimitValue
Archive size25 MB per version
README64 KB
Manifest32 KB
Summary280 characters
Description4000 characters
Keywords12 per package
Account creations5 per hour per network
Sign-in attempts10 per 15 minutes per network
Token creation20 per hour per account
Publishes30 per hour per token, 120 per hour per network
Registry list and search240 per minute per network
Version metadata and downloads120 per minute per network

8. How your credentials are protected

  • Tokens are stored as SHA-256 hashes and compared in constant time. The plaintext exists only in the response that created it.
  • Every publish goes through this site's server. The database key never reaches a browser, and there is no client-side SDK in the page to copy it from.
  • Sessions use httpOnly cookies that script cannot read, and cookie-authenticated actions reject cross-site requests.
  • Publishes, failed attempts, sign-ups, sign-ins and credential changes are written to an audit trail with hashed IP addresses.
  • Browsers get a strict content security policy, HSTS and framing protection in production.

If a token leaks

Revoke it on the dashboard — that takes effect immediately — then create a replacement and update wherever it was stored. Revoked tokens stay in the list, marked Revoked, so you can tell them apart from tokens that are still live.

9. Next steps

  • Libraries — search the registry, or browse what other people have published.
  • Developer dashboard — create and revoke tokens, and see your account.
  • Package API reference — the same endpoints a client uses, with no token required for reads.