Osy#betaRun it on Osyrin: osyrin.com

Reference / Stdlib

Host — the computer a local app runs on

use Osysharp.Host { program "git"; folder "~/Projects"; } · Host.Process · Host.File · Host.Directory · Host.Path · Host.OS · Host.HomeDirectory · Host.DocumentsDirectory · Host.DocumentsSyncedBy · Host.Which(program)

`use Osysharp.Host` gives an app the computer it runs on: run a program (`Host.Process`), read and write files in folders (`Host.File`, `Host.Directory`), and ask which operating system it is (`Host.OS`). The app declares, in the `use` body, each program it may start and each folder it may touch; anything else is refused with the line to add. It exists only on the local platform — an app that uses it does not deploy. A package uses the computer only as far as its own manifest asks and the app that depends on it grants, line by line.

stable2 examples compiled by CIhostlocalprocessfiles

Summary#

Host is the computer a local platform runs on — the user's own machine. An app that says use Osysharp.Host can run a program, keep files in a real folder, and ask which operating system it is on. Everything it may do there is declared, line by line, in the body of that use.

app Toolbox {
  model "**/*.osy";
  use Osysharp.Host {
    program "git";            // a program this app may start — found on the user's search path
    folder  "~/Projects";     // a folder this app may read and write — `~` is the user's home folder
  }
}

string System() => Host.OS.IsWindows() ? "Windows" : Host.OS.IsMacOS() ? "macOS" : "Linux";

string Projects() => Host.Path.Combine(Host.HomeDirectory, "Projects");

// Work kept as files stays out of a documents folder a cloud service syncs.
string Notebooks() => Host.DocumentsSyncedBy == null
  ? Host.Path.Combine(Host.DocumentsDirectory, "Notebooks")
  : Host.Path.Combine(Host.HomeDirectory, "Notebooks");

bool HasGit() => Host.Which("git") != null;

Plain File, Path and Environment keep their meaning — the app's own file store, pure text, the app's own variables. Only a name that starts with Host. touches the computer.

Signature#

use Osysharp.Host {
  program "<name or path>";    // repeat for each program
  folder  "<path>";            // repeat for each folder
  lasting "<name or path>";    // a declared program the app may leave running after it stops
}

bool    Host.OS.IsWindows()          // C#'s OperatingSystem.IsWindows()
bool    Host.OS.IsMacOS()
bool    Host.OS.IsLinux()
string  Host.HomeDirectory           // "/Users/ada" · "C:\Users\ada"
string  Host.DocumentsDirectory      // the user's documents folder, wherever this system keeps it
string? Host.DocumentsSyncedBy       // "iCloud" · "OneDrive" when that service syncs it, else null
string  Host.TempDirectory           // the folder for short-lived files
string? Host.Which(string program)   // the full path of a program, or null when this computer has none

Programs are run with Host.Process — run a program on the computer; files and folders are on Host.File and Host.Directory — files and folders on the computer.

Description#

What the use body declares. Three kinds of line, and nothing else:

linewhat it allowshow it is written
program "git";starting that programa bare name, found on the user's search path — or a full path ("/usr/bin/git"), or a path from the home folder ("~/bin/tool"), which allows exactly that file
folder "~/Projects";reading and writing that folder and everything inside itfrom a named root — ~ (home), documents: (the documents folder), temp: (short-lived files) — or a full path ("/srv/data")
lasting "git";leaving that program running when the platform stops, when it was started with outlives: true (Host.Process — run a program on the computer #outlives)exactly as its program line is written — it adds to that line and never replaces it

There is no line that allows every program or every folder. A relative path is refused when the app is compiled, because it would mean a different folder wherever the platform happened to run. osy explain prints the list (Explaining your app's security).

A folder is declared by a named root, not by a path on one computer. A manifest is written once and read on every user's machine, so the three places that differ per user and per system have names:

declaredis, on the computerread in code as
folder "~/OsyProjects";OsyProjects in the user's home folderHost.HomeDirectory
folder "documents:/Osy Projects";Osy Projects in the documents folder — which Windows may keep somewhere other than under homeHost.DocumentsDirectory
folder "temp:/my-app";my-app in the folder for short-lived filesHost.TempDirectory

An app that keeps its users' work as files declares one such root and builds every path from the matching member: Host.Path.Combine(Host.DocumentsDirectory, "Osy Projects", project.Name).

A documents folder a cloud service syncs. Host.DocumentsSyncedBy answers "iCloud" when a Mac keeps Desktop & Documents in iCloud Drive, "OneDrive" when Windows backs Documents up to OneDrive, and null otherwise. Either service may take a file off the disk and fetch it again only when it is read, so a program reading it waits, and a git repository kept there is known to break. An app that makes folders of work, or runs programs over them, checks it and keeps them under Host.HomeDirectory instead — and declares that folder too. It is asked anew on each read: the person can turn the sync on or off while the app runs.

A call the app did not declare is refused, and the message is the fix. It names what was asked, everything the app declares, and the line to add:

Host.Process.Run cannot start curl: this app has not declared it. It declares: program "git";. To allow it, add program "curl"; to the use Osysharp.Host { … } body in the app's manifest.

A folder is judged by where a path really leads: .. is removed and every symbolic link is followed before the path is compared, so a path cannot climb out of a declared folder and a link inside one cannot lead out of it.

It exists only on the local platform. A local platform (osy launch, osy test) runs on a computer that is the user's own. A deployed platform shares its machine with other apps, so:

  • an app that uses Osysharp.Host does not build for deployment — osy deploy stops on the use line, before anything is sent;
  • a deployed platform does not contain the capability at all, so a call there is refused by name.

It answers only the app's own page. Every web page open in the same browser can send a request to a local platform. A Host.* call runs for a request only when the app's own page sent it, to a name that means this computer (localhost, a *.localhost name, a loopback address); a request from another site's page — or from another local app's page — is refused before anything runs. A function reached without a page (a test, a timer, a workflow step) is the app's own code and is not affected.

What the app declares is for the app's own code. A package the app depends on gets none of it — not a program, not a folder. A package uses the computer only when its own manifest asks and the app grants it, by name: see a package that uses the computer.

Finding a program. Host.Which("claude") answers the full path of a program, or null. It looks where a new terminal would look now — the user's search path is read again, so a program installed while the platform was running is found — then in the folders the common installers use. A declared program that is missing is an ordinary state of a computer: ask Host.Which first and say so on the page, rather than catching the refusal.

Which system. Host.OS.IsWindows(), Host.OS.IsMacOS() and Host.OS.IsLinux() are C#'s OperatingSystem checks. Exactly one is true.

Every Host member runs on the server — the local platform — never in the browser, so it cannot be read in a render block or inside a query. Read it in a function or an action and keep the answer.

A package that uses the computer#

A package — a kit — is somebody else's code running inside your app. It may use the computer, on two conditions: its manifest says what it would do, and your app grants it, line by line.

The package asks, in its own package.osy, in the words an app uses for itself:

package Acme.GitTools {
  version "1.0.0";
  model   "model/**/*.osy";

  use Osysharp.Host {
    program "git";                // it would start git
    lasting "git";                // …and leave it running
    folder  "temp:/acme-git";     // it would read and write this folder
  }
}

That is a request. Nothing is granted by it, and nothing by your app depending on the package.

The app answers, where it names the package — every line the package asks for, each in one of three ways:

app Workbench {
  use Osysharp.Host { program "claude"; folder "documents:/Workbench"; }   // the app's OWN code

  use Acme.GitTools@1 {
    program "git";                   // granted
    folder  "temp:/acme-git/work";   // granted, and narrower: a folder inside the one it asks for
    without lasting "git";           // asked, and declined
  }
}
the app writesit means
the line itself — program "git";granted
folder "temp:/acme-git/work";granted, less: a folder inside the one asked for, written from the same root
without program "git"; · without folder "…"; · without lasting "git";declined — the package's call is refused when it runs. Declining a program declines leaving it running
nothingthe app does not compile — the message names the package, what it asks, and both lines you could write
a line the package does not ask for, or a folder outside (or above) the one it namesthe app does not compile — a grant is never wider than the request, so a package's manifest is the most it can ever do

Acme.GitTools asks to start the program git, to leave the program git running after the platform stops and to read and write the folder temp:/acme-git on the computer this app runs on, and this app has not answered. To allow it, write in the body of use Acme.GitTools@1 { … }: program "git"; lasting "git"; folder "temp:/acme-git"; (a folder inside the one asked for allows less). To use the package without a line, write it after without instead — without program "git"; — and that call of the package is refused when it runs.

When the package's code runs, a Host.* call it makes is judged against what you granted that package — never against what your app declared for itself, and never against what another package was granted. And the other way: your own code gets nothing of what you granted a package. A package you granted nothing is told nothing about the computer at all, Host.HomeDirectory included. Whose call it is, is decided by the code making it: the innermost package on the call stack, as for a package's hosts and secrets (package.osy #grants) — so a function of yours that a package calls back is, for that call, the package's.

A program a package starts is that package's: your code and other packages do not find, watch or end it, and it does not find yours.

Every other rule holds for a package as it does for the app: local platform only, the app's own page only, a folder judged by where a path really leads, a program left running only when lasting allows it.

A package that arrives through another package is granted nothing through it. If Acme.Repos uses Acme.GitTools, your app names Acme.GitTools itself and answers it; a use Acme.GitTools { program "git"; } written in Acme.Repos's manifest is refused there, and means nothing in your app:

Acme.GitTools uses the computer this app runs on (…), and it arrives through Acme.Repos, which this app uses. A package is granted the computer only by the app, by name — Acme.Repos cannot grant it on this app's behalf.

A newer release that asks for more is not pinned: osy update leaves osyrin.lock as it is and says what to write, and the compile refuses it the same way. Answer the new line — grant it or decline it — and update again.

Granting a package any line makes your app one that uses the computer: it runs on a local platform only, and /mcp asks for a key. An app that declines every line deploys; the package's calls are refused there.

osy explain prints, for each package, what it asks and what you granted and declined; osy model --json carries the same as hostGrants; osy kits <Kit> --surface shows what a kit asks before you depend on it.

Examples#

[Test]
void the_computer_answers_for_itself() {
  // Exactly one of the three.
  var systems = (Host.OS.IsWindows() ? 1 : 0) + (Host.OS.IsMacOS() ? 1 : 0) + (Host.OS.IsLinux() ? 1 : 0);
  Assert.Equal(1, systems);

  Assert.True(Host.HomeDirectory != "");
  Assert.EndsWith("Projects", Projects());
  Assert.Null(Host.Which("no-such-program-on-any-computer"));
}

[Test]
void what_the_app_did_not_declare_is_refused_with_the_line_to_add() {
  var program = Assert.Throws<Exception>(() => Host.Process.Run("curl", ["--version"]));
  Assert.Contains("add `program \"curl\";`", program.Message);

  var folder = Assert.Throws<Exception>(() => Host.File.ReadAllText(Host.Path.Combine(Host.TempDirectory, "x.txt")));
  Assert.Contains("outside every folder this app declares", folder.Message);
  Assert.Contains("`folder \"~/Projects\";`", folder.Message);
}

See also#

Related

Host.Process — run a program on the computer

Run a program on the computer a local app runs on. `Host.Process.Run("git", ["--version"])` waits for it and answers a…

Host.File and Host.Directory — files and folders on the computer

Read, write and list real files on the computer a local app runs on, with C#'s `System.IO` names under `Host`:…

Environment — the app's own variables

C#'s `System.Environment`, answering with the APP's values and never the host's: `GetEnvironmentVariable(name)` reads…

Path — the text of a file path

C#'s `System.IO.Path`: build and take apart file paths as text — `Path.Combine`, `GetFileName`…

package.osy

The manifest that makes a git repository a publishable Osy# package. It names the package, says which of the…

Explaining your app's security

Explains your app's declared authorization in plain English — who can read, create, update and delete each entity…