# sexc > sexc is a package manager and build system for C, written entirely in C. It is inspired by npm and pnpm: packages have a sexc.json manifest, a repo can be a workspace of many packages, and dependencies are strict. Anyone can register, publish and install packages on the registry at https://sexc.dev. Every command has machine-readable output, and an MCP server exposes them all as tools. Status: early but working. The build system, workspaces, tests, the CLI, the MCP server and the registry (accounts, tokens, publishing, installing, a lockfile and a shared store) all work today. ## Quickstart - Build sexc from source with only a C compiler: `cc -o bootstrap bootstrap.c && ./bootstrap` - `sexc init mylib` creates a package; `sexc build` builds; `sexc test` tests; `sexc run -- args` runs. - `sexc help --json` describes every command, flag, default and exit code as JSON. ## Commands - `sexc build [packages...] [-j N] [--profile P | --release] [-v] [--json]`: build packages and their dependencies, incrementally and in parallel. Writes compile_commands.json. - `sexc test [packages...] [--timeout S] [--json]`: build, then compile and run every program in each package's tests/ folder. - `sexc run [-- args...]`: build an executable package and run it. - `sexc ls [--json]`: list the workspace's packages. - `sexc graph [--json]`: dependency order and edges. - `sexc init [--name N] [--kind library|executable|ptx|wasm]`: create a package. - `sexc clean [--all]`: remove build output. - `sexc help [command] [--json]`, `sexc version`. - `sexc install [--frozen]`: resolve every registry dependency, write `sexc-lock.json`, fetch into the store, link `.sexc/deps`. `--frozen` fails instead of changing the lock (use it in CI). - `sexc add [@range]... [--to ]` (default range `^`), `sexc remove ... [--from ]`, `sexc update [pkg...]`, `sexc outdated`, `sexc why `. - `sexc pack [pkg] [--out DIR]`: write the .tgz exactly as publish would. `sexc publish [pkg...] [-r] [--dry-run]`: `-r` publishes every non-private workspace package in dependency order, skipping versions already published. `workspace:*` dependencies are rewritten to `^` in the published manifest. - `sexc yank @ [--reason R]`, `sexc unyank @`. - `sexc register --username U --email E --password-stdin`, `sexc login --username U --password-stdin` (or `--token T`), `sexc logout`, `sexc whoami`. - `sexc search `, `sexc info [@version]`. - `sexc token list`, `sexc token create [--name N] [--scope read|publish] --password-stdin`, `sexc token revoke `. - Registry commands take `--registry URL` (default https://sexc.dev). Environment: `SEXC_REGISTRY`, `SEXC_TOKEN` (overrides the credentials file), `SEXC_PASSWORD`, `SEXC_CONFIG_DIR` (default `~/.sexc`, holds `credentials`, mode 0600), `SEXC_STORE` (default `~/.sexc/store`), `SEXC_CURL` (the curl used for HTTPS). - Every command takes `-C ` (before or after the command). Nothing ever prompts. With `--json`, stdout holds exactly one JSON document and stderr stays empty; failures have `"ok": false` and an `errors` array, and compiler messages are parsed into `diagnostics` (file, line, column, severity, message). - Exit codes: 0 ok, 1 failed (a build, a test, a version conflict, an integrity mismatch, a version that already exists), 2 bad usage, 3 workspace or manifest problem (including a dependency that isn't installed), 4 network or registry error. ## sexc.json (a package) - `name` (required): lowercase, digits, `-`, `.`, `_`, optional `@scope/`. Example: `@you/thing`. - `version` (required): semver, like `1.2.3`. - `description`, `license`, `author`, `homepage`, `repository`, `keywords`: metadata. - `kind`: `library` (default), `executable`, `ptx` or `wasm`. A library with no sources is header-only (it lends only its `include/`). - `ptx`: GPU code. Its sources and every library it depends on are compiled by clang for `nvptx64-nvidia-cuda` (freestanding, no libc; the profile's sanitizer and `-g` flags are dropped), linked with llvm-link, and turned into one module, `.sexc/out//ptx/.ptx`. Kernels use `__attribute__((nvptx_kernel))`. `ptx_arch` picks the GPU (default `sm_86`; `$SEXC_PTX_ARCH` overrides). - `wasm`: a freestanding module, `.sexc/out//wasm/.wasm`, linked by `wasm-ld --no-entry --export-dynamic`; the package's own non-static functions are exported, its libraries' aren't. `ldflags` go to wasm-ld. - `targets` (libraries): any of `host`, `nvptx64`, `wasm32` (default all). A library is compiled for a device target only when a ptx or wasm package depends on it (into `obj//`). Dependencies must build for everything their dependent builds for. - Tests are host programs for every kind; a ptx or wasm package's tests get the module's path in `$SEXC_ARTIFACT`. Tools: clang, llvm-link, wasm-ld from PATH, `-21`..`-15`, or `/usr/lib/llvm-*/bin`; override with `$SEXC_CLANG`, `$SEXC_LLVM_LINK`, `$SEXC_WASM_LD`. - `bin`: an executable's output name (default: the name without its scope). - `main`: the source holding main() (default `src/main.c`); left out when linking tests. - `sources`: globs (default `["src/*.c"]`). `tests`: globs (default `["tests/*.c"]`). - `include`: public headers folder (default `include`, when it exists). - `dependencies`: `{"@scope/name": "^1.2.0"}` (npm-style ranges: `^`, `~`, `1.x`, `>=1 <2`, `||`, exact) or `"workspace:*"` for a package in the same workspace. A package only sees the headers of what it declares (and what those declare). - `files`: globs limiting what `sexc pack` includes (sexc.json, README, LICENSE and CHANGELOG are always in). By default every file except hidden ones. - `cflags`, `ldflags`: extra flags for this package. - `private`: true to never publish. ## sexc.json (a workspace root) - `workspaces`: globs of package folders, e.g. `["packages/*", "apps/*"]`. - `cflags`, `ldflags`: shared by every package. - `profiles`: `{"debug": {"cflags": [], "ldflags": []}, "release": {...}}`; pick one with `--profile`. Defaults: debug (-g -O0) and release (-O2 -DNDEBUG). - A folder with only a package sexc.json is a workspace of one. ## Installing and the lockfile - A build holds exactly one version of each package (C has one symbol namespace), so the resolver picks the highest version that satisfies every range in the graph, or fails naming who wants what. - `sexc-lock.json` at the workspace root records each package's exact version, integrity (`sha256:`) and dependencies. Commit it. `sexc build`, `test` and `run` install from it automatically when `.sexc/deps` is missing something. - Packages are unpacked once per machine into `~/.sexc/store/v1/sha256//` (read-only) and symlinked as `.sexc/deps/@scope+name@version`. They build with your compiler and profile into your `.sexc/out`, with `-Wno-error`; their `ldflags` are added when linking. - Put the flags your code needs (e.g. `-D_XOPEN_SOURCE=700`) in the package's own `cflags`: a workspace root's flags are not published. ## MCP - `sexc-mcp` is an MCP server on stdio. Each sexc command is a tool named `sexc_`, with an input schema generated from `sexc help --json`, and each returns the command's JSON report. - Claude Code: `claude mcp add sexc -- /path/to/sexc-mcp --dir /path/to/workspace` ## Writing tests - A test is a C program in tests/; exit 0 means pass. In this repo, include `sexc/test.h` and use `SX_CHECK`, `SX_CHECK_STR`, `SX_CHECK_INT`, ending with `return sx_test_done();`. ## The sexc repo - Every line of code in the sexc repo is C, including the website (sexc.dev is rendered by a design system written in C, with no JavaScript). See AGENTS.md. ## Registry API (https://sexc.dev/v1/) - JSON in and out. Errors are `{"ok": false, "error": "", "code": ""}` with 400, 401, 403, 404, 409, 413 or 429; successes have `"ok": true`. Full reference: https://sexc.dev/docs/api.txt - Auth: `Authorization: Bearer sexc_...` (a token). Only `POST /v1/tokens` takes a password, as `Authorization: Basic base64(username:password)`. Give agents tokens, never passwords; make them at https://sexc.dev/settings/tokens or with `sexc token create`. - `POST /v1/accounts` `{"username","email","password"}`: sign up (username `[a-z0-9-]`, password 10+ characters). - `POST /v1/tokens` `{"name","scope":"publish"|"read"}`: a token, shown once. `GET /v1/tokens`, `DELETE /v1/tokens/:id`, `GET /v1/whoami`. - `GET /v1/packages/:name`: the packument (`latest`, `owners`, `readme`, and `versions` keyed by version: `manifest`, `dependencies`, `integrity`, `size`, `tarball`, `published`, `publisher`, `yanked`). `GET /v1/packages/:name/:version`: one version. - `PUT /v1/packages/:name` `{"version","integrity":"sha256:","tarball":""}`: publish (a publish token; body up to 16 MiB). `POST /v1/packages/:name/:version/yank` `{"reason"}` and `/unyank`. - `GET /v1/tarballs/:name/:version.tgz`, `GET /v1/search?q=&limit=&offset=`, `GET /v1/users/:username`. - Names go in paths literally: `/v1/packages/@ajax/strbuf`. `@scope/` must be your username; unscoped names are first come, first served. Versions are immutable. - Tarballs: entries under `package/`, regular files only (no directories, links or `..`), at most 10 MiB compressed, 50 MiB unpacked, 10 000 files. `package/sexc.json` must match the name and version; every dependency must already be published with a version matching its range; `workspace:` ranges and `"private": true` are refused. - Every registry page has a `.json` twin with the same data: `/packages.json?q=`, `/packages/@ajax/strbuf.json`, `/packages/@ajax/strbuf/v/1.2.0.json`, `/packages/@ajax/strbuf/v/1.2.0/files.json`, `/~ajax.json`. `/llms-full.txt` is this file plus every docs page. ==> https://sexc.dev/ <== ~/sexc.dev/README sexc ==== A package manager and build system for C, written in C. Every package has a `sexc.json`. A workspace holds many of them, and they depend on each other with `"workspace:*"`, the way npm and pnpm do. sexc works out the order, compiles everything in parallel, rebuilds only what a change touched, links, and runs your tests. A registry where anyone can sign up, publish and install is at [packages/](/packages/). ┌─ status ───────────────────────────────────────────────────────────────────┐ │ Early. The build system works, and it builds this website. The registry is │ │ open: sign up, publish, install. Everything here is C, this site, its │ │ design system and the registry server included: no JavaScript, no │ │ Makefiles, no shell scripts. │ └────────────────────────────────────────────────────────────────────────────┘ Quickstart ---------- You need a C compiler and nothing else. In a checkout: ```sh $ cc -o bootstrap bootstrap.c && ./bootstrap $ ./sexc build $ ./sexc test $ ./sexc run sexc-web -- serve ``` `bootstrap` compiles a first sexc in one compiler call, has it build the real one, and links that to `./sexc`. From then on sexc builds itself. A package --------- ```sexc.json { "name": "@sexc/http", "version": "0.1.0", "description": "A small HTTP/1.1 server", "kind": "library", "dependencies": { "@sexc/core": "workspace:*" } } ``` Sources default to `src/*.c`, public headers to `include/`, tests to `tests/*.c`. See [manifest.txt](/docs/manifest.txt) for every field. Why --- - **One language, top to bottom.** The tool, the MCP server, the registry, this website and its design system are all C. - **Strict dependencies.** A package sees the headers of the packages it declares and nothing else, like pnpm's strict `node_modules`. - **Fast.** Parallel, incremental builds that follow every header a file includes. - **Made for agents.** `llms.txt`, an MCP server, `--json` on every command, and `sexc help --json` describing them all. - **Plain text all the way down.** These pages are text files with links in them. `curl sexc.dev/docs/cli.txt` gets you the text without any markup. Files ----- total 4 drwxr-xr-x 856 Oct 26 12:00 [docs/](/docs/) # how to use sexc drwxr-xr-x - Oct 27 12:00 [packages/](/packages/) # everything published drwxr-xr-x 3.0K Oct 27 12:00 [registry/](/registry/) # sign up, install and publish packages -rw-r--r-- 0 Oct 26 12:00 [llms.txt](/llms.txt) # sexc, for language models Keys: alt+shift (or ctrl+option on a Mac) with **h** for this README, **d** docs, **p** packages, **r** registry, **l** llms.txt, **u** your account. -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- README 80L (END) ==> https://sexc.dev/docs/ <== ~/sexc.dev/docs/ total 6 drwxr-xr-x - [../](/) -rw-r--r-- 4.5K Oct 26 12:00 [manifest.txt](/docs/manifest.txt) # the sexc.json format, field by field -rw-r--r-- 2.0K Oct 26 12:00 [workspaces.txt](/docs/workspaces.txt) # monorepos, ordering, profiles, outputs -rw-r--r-- 4.1K Oct 26 12:00 [cli.txt](/docs/cli.txt) # every command and flag -rw-r--r-- 2.6K Oct 26 12:00 [agents.txt](/docs/agents.txt) # MCP, --json, llms.txt, plain text -rw-r--r-- 4.1K Oct 27 12:00 [api.txt](/docs/api.txt) # the registry's HTTP API New here? Read [manifest.txt](/docs/manifest.txt), then [workspaces.txt](/docs/workspaces.txt). Every page here is also plain text: add `?plain`, or fetch it with curl. -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- docs/ 12L (END) ==> https://sexc.dev/docs/manifest.txt <== ~/sexc.dev/docs/manifest.txt sexc.json ========= Every package has one at its root. It's JSON, it's strict, and an unknown field is an error rather than a silent typo. ```sexc.json { "name": "@sexc/web", "version": "0.1.0", "kind": "executable", "bin": "sexc-web", "sources": ["src/*.c", "src/pages/*.c"], "dependencies": { "@sexc/core": "workspace:*", "@sexc/http": "workspace:*", "@sexc/ui": "workspace:*" } } ``` FIELDS ------ name Required. Lowercase letters, digits, `.`, `_` and `-`, optionally under a scope: `@you/thing`. version Required. Semantic versioning: `0.1.0`, `1.2.3-beta.1`. description One line: what it is. kind `library` (the default) builds a static archive others link against; with no sources it is header-only and lends just its `include/`. `executable` builds a program. `ptx` builds GPU code: its sources and every library it depends on are compiled by clang for `nvptx64-nvidia-cuda` and linked into one `.sexc/out//ptx/.ptx`. `wasm` builds a freestanding WebAssembly module, `.sexc/out//wasm/.wasm`, exporting its own non-static functions. ptx_arch `ptx` packages: the GPU architecture, such as `sm_86` (the default) or `sm_90a`. `$SEXC_PTX_ARCH` overrides it. targets Libraries: what they may be compiled for, any of `host`, `nvptx64`, `wasm32`. Default all three. A library is compiled for a device target only when a `ptx` or `wasm` package depends on it, and a package's dependencies must build for everything it builds for. bin Executables: the program's file name. Defaults to the name without its scope. main Executables: the source holding `main()`. Default `src/main.c`. Tests link every other object, so they can call your code. sources Globs, relative to the package. Default `["src/*.c"]`. include Public headers. Default `include` when it exists. Packages that depend on this one see it; nobody else does. tests Globs for test programs. Default `["tests/*.c"]`. Each one is its own program; exit 0 to pass. Tests are host programs for every kind; a `ptx` or `wasm` package's tests find the built module in `$SEXC_ARTIFACT`. dependencies An object of package names to versions: a range such as `"^1.2.0"` from the registry, or `"workspace:*"` for a package in the same workspace. Only libraries can be dependencies. cflags Extra compiler flags for this package's own sources. ldflags Extra linker flags, such as `-lpthread`. Programs that link this package get them too. For a `wasm` package they go to `wasm-ld` (e.g. `--allow-undefined`). private `true` keeps it from ever being published. LAYOUT ------ ``` packages/http/ sexc.json include/sexc/http.h # public: dependents see it src/*.c # private, -I'd for itself tests/*.c # one program each ``` ┌─ strict ───────────────────────────────────────────────────────────────────┐ │ A package compiles with its own `include/` and `src/`, plus the `include/` │ │ of each package it depends on, directly or not. If you didn't declare it, │ │ you can't include it. │ └────────────────────────────────────────────────────────────────────────────┘ GPU AND WASM ------------ ```sexc.json { "name": "@you/kernels", "version": "0.1.0", "kind": "ptx", "ptx_arch": "sm_86", "dependencies": {"@you/abi": "workspace:*"} } ``` Device code is freestanding C: no libc, `-ffreestanding`, and the profile's sanitizer and debug flags are left out. A kernel is a function with `__attribute__((nvptx_kernel))`. clang, llvm-link (for ptx) and wasm-ld (for wasm) are found on PATH, as `-21` .. `-15`, or in `/usr/lib/llvm-*/bin`; set `$SEXC_CLANG`, `$SEXC_LLVM_LINK` or `$SEXC_WASM_LD` to choose. The workspace root has a manifest of its own: see [workspaces.txt](/docs/workspaces.txt). -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- manifest.txt 130L (END) ==> https://sexc.dev/docs/cli.txt <== ~/sexc.dev/docs/cli.txt NAME ---- sexc - build, test, install and publish C packages SYNOPSIS -------- ``` sexc [options] [package...] ``` COMMANDS -------- build [package...] Build the named packages and everything they depend on; all of them when none are named. A package is named by its `name`, its `bin` or its folder. test [package...] Build, then compile and run each test program. Output is shown for failures. Tests time out after 60 seconds. run [-- args...] Build a program and run it with `args`. ls List the workspace's packages: name, version, kind, folder and dependencies. graph The build order, and which package depends on which. init [--name N] [--kind library|executable|ptx|wasm] Start a package: a `sexc.json`, a source, a header and a test. `ptx` starts with a kernel, `wasm` with an exported function. clean Remove `.sexc/out`. install [--frozen] Resolve every registry dependency in the workspace, write `sexc-lock.json`, fetch into the store and link `.sexc/deps`. With `--frozen`, fail instead of changing the lock. add [@range]... [--to P] Add dependencies (default `^`) to the package here, or to `P`, then install. remove ... [--from P] Remove dependencies, then install. update [pkg...] Re-resolve the named packages (or all) to the newest versions their ranges allow. outdated Locked, wanted and latest version of each dependency. why Which workspace packages pull a dependency in, and through what. pack [pkg] [--out DIR] Write the package's `.tgz` as it would be published. publish [pkg...] [-r] [--dry-run] Pack and upload. `-r` publishes every non-private package in dependency order, skipping versions already published. yank @ [--reason R] Hide a version from new installs; `unyank` undoes it. register --username U --email E --password-stdin Make an account, then log in. login --username U --password-stdin | --token T Store a token in `~/.sexc/credentials`. logout Revoke the stored token and forget it. whoami Who the stored token belongs to. search Search the registry. info [@version] A package's versions, dependencies and owners. token list | create [--name N] [--scope S] | revoke Manage API tokens. help [command] [--json] Help for every command, or one. With `--json`, a description of every command, argument and flag that a program can read. version Print the version. OPTIONS ------- --json Print one JSON document on stdout and nothing else: results, errors, and compiler diagnostics as `file`, `line`, `column`, `severity`, `message`. -j N, --jobs N Run N compilers at once. Default: one per CPU. --profile NAME Build with a profile from the root manifest: `debug` (the default), `release`, `asan`. --release Short for `--profile release`. -v, --verbose Show every command as it runs. --registry URL Use another registry. Default `https://sexc.dev`, or `$SEXC_REGISTRY`. EXIT STATUS ----------- 0 Everything built, or every test passed. 1 A build or a test failed. 2 The command line was wrong. 3 A manifest or the workspace is wrong: bad JSON, an unknown field, a missing dependency, a cycle. 4 The registry couldn't be reached, or refused the request. EXAMPLES -------- ``` $ sexc build --release $ sexc test @sexc/core $ sexc build --json | jq '.diagnostics' $ sexc run sexc-web -- serve --port 8080 $ sexc add @ajax/strbuf $ sexc publish -r ``` SEE ALSO -------- [manifest.txt](/docs/manifest.txt), [workspaces.txt](/docs/workspaces.txt), [agents.txt](/docs/agents.txt), [api.txt](/docs/api.txt), [registry/](/registry/) -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- cli.txt 165L (END) ==> https://sexc.dev/docs/workspaces.txt <== ~/sexc.dev/docs/workspaces.txt Workspaces ========== A workspace is a folder with a `sexc.json` that has `workspaces` in it. sexc finds it by walking up from wherever you are, so commands work from any folder inside. A lone package with no workspace above it works too. ```sexc.json { "name": "sexc-monorepo", "private": true, "workspaces": ["packages/*", "apps/*"], "cflags": ["-std=c17", "-Wall", "-Wextra", "-Werror"], "profiles": { "debug": { "cflags": ["-g", "-O0"] }, "release": { "cflags": ["-O2", "-DNDEBUG"] } } } ``` ROOT FIELDS ----------- workspaces Globs of package folders, like `["packages/*", "apps/*"]`. Any folder they match that has a `sexc.json` is a package. cflags Flags for every compile in the workspace, before each package's own. ldflags Flags for every link. profiles Named sets of `cflags` and `ldflags`: `debug` is used unless you pass `--profile` or `--release`. HOW A BUILD GOES ---------------- - Packages are ordered so each builds after what it depends on. A cycle is an error that names every package in it. - Every source in every package compiles at once, one compiler per CPU. Errors from parallel compiles never interleave. - A file recompiles when it, any header it includes, or its command line changed. Nothing else does. - Libraries are archived, programs linked, each only when an input changed. - Each source file is compiled with `-DSX_PACKAGE_NAME` and `-DSX_PACKAGE_VERSION` from its package's manifest. OUTPUT ------ ``` .sexc/out//obj/... objects, with .d and .cmd .sexc/out//lib/ lib.a .sexc/out//bin/ programs .sexc/out//test/ test programs compile_commands.json for clangd and editors ``` Everything under `.sexc/` can be deleted at any time; `sexc clean` does it. -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- workspaces.txt 63L (END) ==> https://sexc.dev/docs/agents.txt <== ~/sexc.dev/docs/agents.txt For agents ========== sexc is built to be driven by programs as much as by people. Nothing asks a question interactively, every command can answer in JSON, and every page of this site is plain text if you ask for it. MCP --- `sexc-mcp` is an MCP server over stdio. It reads `sexc help --json` and offers every command as a tool (`sexc_build`, `sexc_test`, `sexc_ls`, ...), so new commands appear without changing the server. ``` $ claude mcp add sexc -- $PWD/.sexc/out/debug/bin/sexc-mcp --dir $PWD # or, in any MCP client's config: # { "command": "sexc-mcp", "args": [] } ``` JSON ---- With `--json`, a command prints one JSON document and nothing else. Build errors come back as data: for each diagnostic its `file`, `line`, `column`, `severity` and `message`, plus the compiler's full output and the exact command that failed. ``` $ sexc help --json # every command, argument and flag $ sexc build --json # results and diagnostics $ sexc ls --json # the packages ``` The registry ------------ The registry's HTTP API is JSON, documented in [api.txt](/docs/api.txt). Every package page has a `.json` twin with the same data, and `sexc search`, `info`, `add`, `install` and `publish` take `--json` and are MCP tools like the rest. Give an agent a token, not a password: make one on [settings/tokens](/settings/tokens) and pass it as `SEXC_TOKEN`. llms.txt -------- [/llms.txt](/llms.txt) describes sexc for language models: what it is, the manifest, the commands, the registry API, and where to read more. [/llms-full.txt](/llms-full.txt) is that plus every page of these docs. Plain text ---------- Fetch any page without asking for HTML and you get text: headings underlined, links as `[label](href)`, code fenced. Browsers can add `?plain`. ``` $ curl https://sexc.dev/docs/cli.txt ``` ┌─ the rule ─────────────────────────────────────────────────────────────────┐ │ Every line of code in sexc is C. That's in the repository's `AGENTS.md`, │ │ and it applies to agents too: no scripts in other languages, not even to │ │ glue things together. │ └────────────────────────────────────────────────────────────────────────────┘ -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- agents.txt 70L (END) ==> https://sexc.dev/docs/api.txt <== ~/sexc.dev/docs/api.txt The registry API ================ JSON over HTTPS at `https://sexc.dev/v1/`. It is what `sexc install` and `sexc publish` use, and anything else may use it too. - **Auth.** `Authorization: Bearer sexc_...` with a token. Only `POST /v1/tokens` takes a password, as Basic auth. - **Names** go in paths as they are: `/v1/packages/@ajax/strbuf`. - **Errors** are `{"ok": false, "error": "...", "code": "..."}`: a sentence that says what to do, and a stable slug. 400 bad input, 401 no or bad credentials, 403 not allowed, 404, 409 conflict, 413 too large, 429 slow down. - **Successes** have `"ok": true`. ENDPOINTS --------- POST /v1/accounts Sign up. Body `{"username", "email", "password"}`. 201 `{"ok": true, "username"}`; 409 `username_taken` or `email_taken`. Five an hour per address. POST /v1/tokens Make an API token. `Authorization: Basic` with `username:password` (or email). Body `{"name": "laptop", "scope": "publish"}`, both optional; scope is `publish` or `read`. 201 with `token` (shown once), `id`, `name`, `scope`, `username`. Ten failed logins an hour per address, then 429. GET /v1/tokens Your tokens, never their secrets: `id`, `name`, `scope`, `created`, `last_used`. DELETE /v1/tokens/:id Revoke one. GET /v1/whoami `username`, `email`, `scope`, and `token_id` (the token you sent). GET /v1/packages/:name The packument: `name`, `description`, `keywords`, `license`, `homepage`, `repository`, `owners`, `latest`, `created`, `updated`, `readme`, and `versions`, keyed by version in semver order, each with `manifest`, `dependencies`, `integrity`, `size`, `tarball`, `published`, `publisher`, `yanked`, `yank_reason`. GET /v1/packages/:name/:version One version object, plus `name`. PUT /v1/packages/:name Publish. A publish token, and body `{"version", "integrity": "sha256:", "tarball": ""}` up to 16 MiB. 201; 409 `version_exists`; 403 `wrong_scope` or `not_owner`; 400 with the reason for anything wrong in the tarball or its manifest. POST /v1/packages/:name/:version/yank Body `{"reason"}`, optional. The version stays downloadable for lockfiles; resolution skips it. POST /v1/packages/:name/:version/unyank Undo a yank. GET /v1/tarballs/:name/:version.tgz The tarball, `application/gzip`, cached forever. GET /v1/search?q=&limit=20&offset=0 `total` and `results`: `name`, `version`, `description`, `keywords`, `updated`. Searches names, descriptions, keywords and READMEs; no `q` lists the most recently updated. GET /v1/users/:username `username`, `created` and their `packages`. PUBLISHING BY HAND ------------------ ``` $ sexc pack # writes ajax-strbuf-1.2.0.tgz $ curl -X PUT https://sexc.dev/v1/packages/@ajax/strbuf \ -H "Authorization: Bearer $SEXC_TOKEN" -H 'Content-Type: application/json' \ -d "{\"version\":\"1.2.0\",\"integrity\":\"sha256:$(sha256sum < ajax-strbuf-1.2.0.tgz | cut -c1-64)\",\"tarball\":\"$(base64 -w0 ajax-strbuf-1.2.0.tgz)\"}" ``` `sexc publish` does all of that, and rewrites `workspace:*` dependencies to real ranges first. RULES ----- - `@scope/` is your username: only you publish under it. Unscoped names are first come, first served. - Versions are immutable. Yank a bad one; publish a fixed one. - A tarball holds `package/sexc.json` and the rest under `package/`: regular files only, no links, no `..`, at most 10 MiB compressed, 50 MiB unpacked and 10 000 files. - Every dependency must already be published, with a version matching its range. - Packages marked `"private": true` are refused. THE WEBSITE ----------- The pages under [packages/](/packages/) have `.json` twins with the same data: `/packages.json?q=`, `/packages/@ajax/strbuf.json`, `/packages/@ajax/strbuf/v/1.2.0.json`, `/packages/@ajax/strbuf/v/1.2.0/files.json`, `/~ajax.json`. -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- api.txt 107L (END) ==> https://sexc.dev/registry/ <== ~/sexc.dev/registry/ total 3 drwxr-xr-x - [../](/) drwxr-xr-x - Oct 27 12:00 [packages/](/packages/) # everything published -rw-r--r-- 4.1K Oct 27 12:00 [api.txt](/docs/api.txt) # the registry's HTTP API The registry ============ Anyone can sign up and publish. Your username is your scope: packages named `@you/...` are yours alone. [Sign up](/signup) on the web, or from a terminal: ```sh $ printf '%s\n' "$PASSWORD" | sexc register --username you --email you@example.com --password-stdin $ printf '%s\n' "$PASSWORD" | sexc login --username you --password-stdin $ sexc whoami ``` Passwords come from stdin, never the command line: sexc never prompts. The token it gets is kept in `~/.sexc/credentials` (mode 0600). In CI, make a token on [settings/tokens](/settings/tokens) and set `SEXC_TOKEN`. Installing ---------- ```sh $ sexc add @ajax/strbuf # adds "^1.2.0" to dependencies, installs $ sexc install # what sexc-lock.json says $ sexc install --frozen # in CI: fail if the lock is stale $ sexc outdated $ sexc why @ajax/mem ``` - **One version of each package.** C has one namespace for symbols, so a build holds exactly one version of each dependency: the highest that satisfies every range that asks for it. If none does, sexc says which packages disagree. - **A lockfile.** `sexc-lock.json` pins every version and its SHA-256, checked on every install. - **One store.** Like pnpm, each version is unpacked once per machine, in `~/.sexc/store`, and linked into `.sexc/deps`. - **Built your way.** Dependencies build with your compiler and profile, and their warnings never fail your build. Publishing ---------- ```sh $ sexc pack --dry-run # what would go in $ sexc publish # this package $ sexc publish -r # every package in the workspace, in order $ sexc yank @you/thing@1.0.1 --reason "breaks free()" ``` - `workspace:*` dependencies become `^` in what's published. - Everything in the package folder goes in, except hidden files and folders; list `"files"` in `sexc.json` to narrow it. - Versions are immutable. A yanked version stays installable for lockfiles that already name it. - Mark a package `"private": true` and it is never published. ┌─ for agents ───────────────────────────────────────────────────────────────┐ │ Every command takes `--json`, and `sexc-mcp` offers them all as tools. The │ │ HTTP API is in [api.txt](/docs/api.txt); every package page has a `.json` │ │ twin. │ └────────────────────────────────────────────────────────────────────────────┘ -- [README](/) [docs/](/docs/) [packages/](/packages/) [registry/](/registry/) [llms.txt](/llms.txt) [login](/login) -- registry/ 65L (END)