Osy#betaa language · its runtime Osyrin · a hosted platform
Why Osy#Built for agentsAgents as declarationsWorkflows that waitRuns exactly onceSecure by defaultNothing to mockThe editor is the compilerUI in the languageDocuments are dataOne program

Reference / Project

Usage statistics (what is sent, and the two ids that let us count you once)

osy telemetry off

The toolchain sends usage counts — command names, versions, OS, error codes and durations — with two random ids: one per installation, so a person is counted once, and one per project, so a started app can be followed to its first deploy. 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

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.

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.

Two random ids, disclosed. Each event carries an installation id — a random value written to ~/.osy/config.json the first time the toolchain runs — and, from a project, a project id — a random value osy init writes into that project's osyrin.json. Neither is derived from anything: not your hardware, your user name, your e-mail or the project's name. They exist for two questions the counts alone cannot answer: how many people are trying the toolchain (one installation id, counted once, however many runs), and does an app that gets started get finished — a project id joins that project's init, 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. What they are never 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 — counted by region, never stored by event. 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. What is stored is the count, not the event: there is no row that says one run happened in one place at one time, so there is nothing that could be traced to a person however few people a region has. 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 three 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 four timestamps, the region); and the weekly counts, which are all that remains after the ninety days. 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.

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 (+ 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…