# Host — the computer a local app runs on

> `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.

<!-- id: stdlib-host · area: stdlib · stability: stable · html: https://osysharp.com/reference/stdlib/host/ -->

## Summary        {#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`.

```osy title="an app that may start git and use one folder" test app=stdlib-host
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      {#signature}
```osy syntax
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](https://osysharp.com/reference/stdlib/host-process/); files and folders are on [Host.File and Host.Directory — files and folders on the computer](https://osysharp.com/reference/stdlib/host-files/).

## Description    {#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](https://osysharp.com/reference/stdlib/host-process/) `#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](https://osysharp.com/reference/local/explaining-your-app/)).

**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.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](#packages).

**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   {#packages}
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:

```osy title="a package says what it would do on the computer" syntax
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:

```osy title="the app grants one line, narrows one, declines one" syntax
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.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](https://osysharp.com/reference/project/package/) `#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       {#examples}
```osy title="what the computer answers, and what it refuses" run app=stdlib-host
[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       {#see-also}
- [Host.Process — run a program on the computer](https://osysharp.com/reference/stdlib/host-process/) — run a program and read what it printed
- [Host.File and Host.Directory — files and folders on the computer](https://osysharp.com/reference/stdlib/host-files/) — files and folders on the computer
- [Environment — the app's own variables](https://osysharp.com/reference/stdlib/environment/) — the app's own variables, which never include the computer's
- [Path — the text of a file path](https://osysharp.com/reference/stdlib/path/) — the text of a path, with no computer involved
- [package.osy](https://osysharp.com/reference/project/package/) — the `use` line and what its body is for
