Osy#betaRun it on Osyrin: osyrin.com

Reference / Project

Usage statistics (what is sent, and the three random ids — one per installation, project and session)

osy telemetry off

The toolchain sends usage counts — command names and their flag names, versions, OS, error codes, durations, the size of an answer and whether a person in a terminal, Studio or an AI agent ran the command — with three random ids: one per installation, so a person is counted once; one per project, so a started app can be followed to its first deploy; and one per session, so runs made together can be read in order. Never your source, your data, your paths, your name or your address. One command turns it off, another resets the ids, and the first run tells you it is on.

previewprojecttoolchainprivacy

Summary#

The toolchain sends usage counts so that the verbs people reach for, the versions they run and the errors they hit can be counted — and so that the people trying it can be counted once, and an app somebody starts can be followed to the moment it first deploys. Two random ids make that possible, and this page says exactly what they are and what they are not. It never sends your source, your data, your paths, your name, your machine name or your address. osy telemetry off stops it, durably; osy telemetry reset replaces the ids with new ones; and the first run of the toolchain says that it is on and how to turn it off.

Signature#

osy telemetry off      # stop sending, durably
osy telemetry on       # start again
osy telemetry reset    # new installation id — the old one's events can no longer be joined to yours
osy telemetry status   # the setting, where it is stored, your version and the newest one the receiver has named

Description#

What is sent. Each run of an osy or osyrin verb records one event, locally, immediately — sending it is a separate step (see "Sent in batches" below):

fieldexamplewhy
the verblaunch, check, initwhich parts of the toolchain are used at all
the toolchain version0.9.3whether a report is about the current release
the operating system and architecturedarwin/arm64which builds have to exist
the exit code, and an error code when there is oneCommandParseExceptionwhich refusals people hit most, so their wording can be fixed
the duration2140 mswhich verbs are slow
the installation id9f1c… (32 random hex characters)so a person is counted once — see "Two random ids" below
the project id4a7e…, when the verb ran inside a projectso a started app can be followed to its first deploy
the session idc81b…, shared by runs less than 30 minutes apartso runs made together can be read in order — whether the next run after a refusal succeeded, which is how a confusing message is found
the command's shapekit --atoms, app compile --server, checkwhich flags people actually reach for. The flag NAMES only: a value, a path, an app name or a search term is never part of it
the size of the answer4096 bytes, when the answer was captured rather than streamedwhich answers are too big to read in one go
who ran itterminal, studio or agenthow much of the toolchain is used through Studio and through AI agents, and how far each gets a project — see "Who ran it" below

What is never sent. Your source. Your data. File or directory paths. App names, entity names, function names, or any other identifier from your project. Your user name, machine name, IP address or e-mail. The text of a diagnostic (only its type — the class of what went wrong, e.g. CommandParseException, never the message that came with it, which can quote back whatever the command was given). Anything typed into a prompt.

Who ran it — one of three words, and no personal data. Each event says whether the command came from a person in a terminal (terminal), from Studio (studio) or from an AI coding agent (agent). It is decided from the environment the toolchain was started in and nothing else: Studio marks every program it starts (the local platform sets OSY_STARTED_BY to the app that started a program, and Studio is studio), and Claude Code sets CLAUDECODE=1 in every program it runs, as several other agents set AGENT. A command an agent runs inside a Studio build counts as Studio's. The field holds one of those three words and nothing more — never which agent, never the value of any variable, never anything else about your computer — and the receiver stores any other value as nothing. It collects no personal data.

One deliberate, narrow exception: what you typed into osy docs. A search term for the reference is not a prompt and not project data — it is a question about the toolchain itself — but it is still text you typed, so it travels as its own separate, explicitly-named field rather than being folded silently into the general shape: what you asked ("Session.SignOut"), and whether the reference answered it (Page, Ambiguous, Miss, …). This is the field that turns "the reference sometimes fails" into "the reference fails on THESE terms" — the same gap osy docs --misses cannot see on its own, because that command only ever looks at one machine's own local log.

Sent in batches, not on every run. The local event above is free — it is a few bytes on disk. Actually sending anything anywhere is rate-limited to happen at most a few times an hour, at the very most, whichever run happens to be the one due — a session running many commands back to back does not generate a burst of network traffic for each one. A send that fails (the destination is unreachable) is not retried sooner; it waits for the next window, the same as a successful one would.

Three random ids, disclosed. Each event carries an installation id — a random value written to ~/.osy/config.json the first time the toolchain runs — from a project, a project id — a random value osy init writes into that project's osyrin.json — and a session id, a random value shared by runs made less than thirty minutes apart and replaced after any longer gap. None is derived from anything: not your hardware, your user name, your e-mail or the project's name. They exist for three questions the counts alone cannot answer: how many people are trying the toolchain (one installation id, counted once, however many runs), does an app that gets started get finished — a project id joins that project's init, its first green osy validate, its first green osy check, its first osy launch and its first deploy into one story, and the time between them. From the two together: projects per person, and how far the typical person gets. The session id answers the third: did the tool answer you — whether the run after a refusal succeeded, whether the same question had to be asked four times, whether one answer was paid for twice. That is a fact about the toolchain's own messages, and it cannot be read from events that cannot be put in order. ⚠ It is a rolling window, not your shell: a run is its own process, so the id is reused while runs keep coming and a new one starts after a gap — nothing reads your process tree, your terminal or your parent program. What none of them is ever used for: joining to a person. There is no name, address or account anywhere in the data to join them to, and nothing in the toolchain sends one. Because a random id that persists is pseudonymous rather than anonymous, this page says so rather than calling the data anonymous: it identifies an installation, not a human, and osy telemetry reset breaks even that link whenever you like. Raw events are kept for ninety days and then only the weekly counts remain.

Where people use it — on the raw event for ninety days, then only as a count. The receiving server learns where a batch came from and adds it to a count: this region, this week, this verb. The country and its first subdivision are kept — a US state, a Swedish county, a German Land — because a map of where the toolchain is used is one of the things the statistics exist for, and "United States" says much less than fifty states do. The connection's address is used for one lookup at the edge and discarded before anything is stored; where the ingress sits behind a CDN the location arrives as a header and the address is never seen at all. The raw event carries the two codes for its ninety days, beside the rest of that run; after the purge only the weekly counts remain, and a count says this region, this week, this verb and nothing about any single run. Neither form holds anything to trace to a person: there is no name, address or account anywhere in the data to join a region to. The "never your IP address" above stays literally true — it is not a field, it is not stored, and it is not sent by the toolchain.

Where it goes, and for how long. Batches are sent at most once an hour — and right away after osy init, osy launch and a deploy, the three moments the funnel is made of, so a first try that goes no further still counts — to osysharp.com, or to whatever OSY_TELEMETRY_URL names — a self-hosted platform receives its own toolchain's statistics through the same route (/api/telemetry/v1), so an organisation that runs its own can keep them entirely in-house. The receiver keeps four things: the raw events for ninety days and then deletes them; one row per installation id and one per project id (first seen, last seen, the funnel's five timestamps, who started it, the region); the weekly counts, and the weekly runs by who ran them; and, per week and error code, how often a refusal was followed by a working run of the same command — which is how we find out which of our error messages actually help. The last two are all that remains after the ninety days, and neither carries an id, a place or anything you typed: an error code is one of our own, from a fixed list. A batch that cannot be sent — no network, the receiver down — waits on disk for the next hour and costs the command that tried nothing beyond a three-second cap; the waiting log is bounded, and past a few thousand lines the oldest are dropped.

The update check is a separate request, to the release channel, and it is anonymous to us. Once a day the toolchain asks GitHub's releases page for osysharp/cli — the same place Homebrew, winget and the install script download from — which version is the newest. GitHub sees a request with the toolchain's user agent and nothing else; the statistics receiver is not involved and never learns who asked. When the answer is newer than the one you are running, the toolchain prints one line, once a day, before the command's own output:

a newer osy is available: 0.9.1 (you have 0.9.0) — osy upgrade
  what's new: osy whats-new   ·   https://github.com/osysharp/cli/releases/tag/v0.9.1

osy upgrade is the one command that installs it on every platform — through the same download door, checked against the release's checksums, swapped in place (Upgrading the toolchain). osy whats-new prints the release's notes in the terminal, from the same public page, and osy whats-new 0.9.1 reads a particular version's. It stays on when statistics are off, because it is not a statistic; "updateCheck": false in ~/.osy/config.json turns it off for a machine that wants no request it did not type, and osy telemetry status shows the running and the latest published version either way.

Disclosed at first run. The first time the toolchain runs it prints one line saying that usage statistics are on, that they carry a random installation id, and that osy telemetry off stops them. It does not ask a question, because a question in the middle of osy init is exactly what nobody reads; it tells you, once, where you will see it.

Where the setting lives. ~/.osy/config.json, in the toolchain's own configuration directory, not in your project — so it is a choice you make once per machine, and it is not something a checkout can quietly turn back on. osy telemetry off stops LOCAL RECORDING too, not only sending — turning it off means the toolchain stops writing these events at all, not just stops mailing them. Where there is no home directory to keep a setting in — a container, a CI job, a build machine's scheduled tasks — OSY_TELEMETRY=off in the environment (or the cross-tool DO_NOT_TRACK=1) does the same for every run that inherits it, and neither variable can ever turn statistics back on.

Only a downloaded toolchain sends. A toolchain you built from source yourself records locally and sends nothing, unless OSY_TELEMETRY_URL names where to.

Feedback is separate, and it is yours to send. osy feedback opens a prefilled issue on the toolchain's public tracker (github.com/osysharp/cli), with your version and the last diagnostic the toolchain hit — an internal fault, or the last "✗" refusal a command printed — already filled in — a real GitHub issue form you still review and edit before you submit it. That is a message you write and send; it is not a statistic, and turning statistics off does not affect it. Pass --print-url to see the link without opening a browser.

osy feedback --full adds a diagnostic bundle, and the issue never holds it. The bundle is a zip built on your machine: the last diagnostic, the tail of the local run log, the project's osyrin.json and osyrin.lock and a list of its files, and the log tails of the local platform serving it. Never your .secrets, never an operator key, never the database. Your .osy sources go in only with --with-source. The contents are listed before anything leaves the machine, and then the zip is uploaded privately to osysharp.com — into storage only the maintainers can read — and the issue carries a short bundle id in its place. Anything attached to an issue on a public repository is public, which is why the file is never attached. --no-upload keeps the zip on disk instead, for you to attach or mail yourself.

$ osy feedback --full "validate refuses a valid app"
diagnostic bundle — 14,212 bytes, 9 entries:
  manifest.txt
  toolchain.json  (131 bytes)
  last-diagnostic.json  (188 bytes)
  …
bundle uploaded privately as fb-3f9a2c17d0 — only the maintainers can read it; the issue carries the id.
Opening a prefilled issue on osysharp/cli — review it before you submit.

Examples#

$ osy telemetry status
anonymous statistics: on
  sends: verb · version · os/arch · exit code · error code · duration · who ran it (terminal, studio or agent) (+ the search term, for `osy docs` only)
  never: source · data · paths · names · addresses · anything else typed
  ids:   a random installation id · a random project id — see above
  off:   osy telemetry off

$ osy telemetry off
anonymous statistics: off (stored in ~/.osy/config.json)

$ osy feedback "the docs page for X is wrong about Y" --print-url
https://github.com/osysharp/cli/issues/new?title=…&body=…

See also#

Related

Hosting an Osy# app

You never set up a database. You never started a web server. That wasn't a step we skipped — it's the product. Your…

The local loop (running your app on your own machine)

Everything the `osy` CLI does to the project in front of you — run it, look at what it did, check it before you ship…

The inner loop

The local develop-and-debug loop for an Osy# app — for the person in the editor and the coding agent at the CLI alike…

Upgrading the toolchain

Replaces the osy you are running with a newer release — downloaded, checked against the release's checksums, and…