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 nonePrograms 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:
| line | what it allows | how it is written |
|---|---|---|
program "git"; | starting that program | a 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 it | from 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:
| declared | is, on the computer | read in code as |
|---|---|---|
folder "~/OsyProjects"; | OsyProjects in the user's home folder | Host.HomeDirectory |
folder "documents:/Osy Projects"; | Osy Projects in the documents folder — which Windows may keep somewhere other than under home | Host.DocumentsDirectory |
folder "temp:/my-app"; | my-app in the folder for short-lived files | Host.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.Runcannot startcurl: this app has not declared it. It declares:program "git";. To allow it, addprogram "curl";to theuse 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.Hostdoes not build for deployment —osy deploystops on theuseline, 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 writes | it 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 |
| nothing | the 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 names | the 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.GitToolsasks to start the programgit, to leave the programgitrunning after the platform stops and to read and write the foldertemp:/acme-giton the computer this app runs on, and this app has not answered. To allow it, write in the body ofuse 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 afterwithoutinstead —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.GitToolsuses the computer this app runs on (…), and it arrives throughAcme.Repos, which this app uses. A package is granted the computer only by the app, by name —Acme.Reposcannot 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#
- Host.Process — run a program on the computer — run a program and read what it printed
- Host.File and Host.Directory — files and folders on the computer — files and folders on the computer
- Environment — the app's own variables — the app's own variables, which never include the computer's
- Path — the text of a file path — the text of a path, with no computer involved
- package.osy — the
useline and what its body is for