# Where your app may connect — the .egress file

> An application's own outbound allow-list and deny-list, kept in the project's .egress file. The osy egress verbs edit it; a local compile applies it to the local platform, and osy deploy carries it to where the app runs and applies it there in the same commit as the rest of the deploy.

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

## Summary        {#summary}
Your app's own code may call any public host by default — the platform blocks only its internal addresses. An
administrator narrows that with **egress rules**: `allow` a host and the app may reach only what is allowed; `deny` a
host and it may never reach it, whatever else says so.

The rules live in a file beside `app.osy` called `.egress`. It is the durable record: `osy egress` edits it, a local
`osy compile` applies it to your local platform, and **`osy deploy` carries it with the app and applies it where the
app runs**. A rule you wrote is the rule in force, on your machine and in every environment you deploy to.

## Signature      {#signature}
```console
$ osy egress allow api.partner.example       # permit a host — the FIRST allow closes the app
$ osy egress deny  *.tracker.example         # refuse a host, whatever else allows it
$ osy egress remove api.partner.example      # drop every rule naming that host
$ osy egress list                            # the rules, what they mean, and every package's granted hosts
$ osy egress observed                        # where the app has actually tried to go
```

## Description    {#description}

### What the file says   {#file}
One rule per line, `allow` or `deny` and one host. `#` starts a comment.

```text title="closed to the partner's hosts, except one" syntax
# only these hosts
allow *.partner.example
allow api.payments.example

# …and never this one, even though the wildcard covers it
deny blocked.partner.example
```

A host is `api.example.com`, or `*.example.com` for every subdomain — **not** the apex: `*.example.com` does not match
`example.com`. A URL, a port, a bare `*` or a wildcard anywhere but the front is refused by name rather than read as
something else. So is a line with no verb: `api.example.com` on its own could mean either, and guessing wrong in the
permissive direction turns a deny-list into an allow-list.

### What a set of rules means   {#posture}
**Only an `allow` closes the app.** That is the one rule to remember, and `osy egress list` says which posture you are
in so you never have to work it out:

| The file holds | The app may reach |
|---|---|
| nothing, or no file | any public host — **open** |
| only `deny` lines | any public host **except** the denied ones |
| one or more `allow` lines | **only** the allowed hosts, minus any denied |

⚠ The first `allow` is a bigger change than one line looks: from then on everything not allowed is refused. Run the app
open first and read `osy egress observed` — every host it has actually tried to reach — before you close it.

A **deny binds your packages too**: a package reaches only what you grant it in `use X { egress "…"; }`, and a deny
refuses that host even so. Your own `allow` list does not narrow a package's grant — the grant already is your decision
about that package ([[project-package#browser-egress]]).

### It reaches where the app runs   {#deploy}
`osy deploy` reads `.egress` with the rest of the project and the deploy **carries it**. The zone that builds the
version applies the rules to the environment's database **in the same commit** as the version's other data, so the
version goes live with its rules or not at all:

```console
$ osy deploy --env prod
Building billing locally…
Built billing: blob 3f2a9c1b04e7 (412311 bytes), 0 package archive(s), in 2140 ms
Its own egress rules (.egress) travel with it and are applied where it runs: CLOSED to 2 allowed host(s), minus 1 denied
```

- **The file is the whole posture.** A deploy with no `.egress` file sets **no** rules of its own — any the app had in
  that environment are removed. A line you delete is a rule that is gone there too, on the next deploy.
- **A line that cannot be read refuses the deploy**, naming the line, before anything is uploaded. Shipping the lines
  that could be read would put a posture in force that no file describes.
- **A change to `.egress` is a new version.** Two deploys that differ only in their rules are not the same deploy.
- The rules are shown on the application's **Config** tab in the console, per environment, as the app's own — beside the
  hosts its packages are granted.

### Local: applied on every compile   {#local}
On your machine the verbs apply the file to a running local platform immediately, and `osy compile` re-applies it after
every compile — so it survives a reset of the local instance with nothing to remember.

### An app that sends nothing   {#nothing-to-govern}
The rules govern what the app sends: its own `Http.*`, `Imap.*` and `Smtp.*` calls (all under `use Osysharp.Http;`), the
calls of the packages it grants hosts, and the browser hosts it grants a package. An app that does none of these has
nothing to govern, so it carries no table for rules at all. Write a `.egress` file for it anyway and it is kept: the
verbs and `osy compile` say the rules were not applied and why, and the first compile or deploy of a version that does
send applies them.

### When your organization sets rules too   {#organization}
An application may also be bound by rules its **organization** sets. Those are a ceiling: your `allow` cannot reach past
them, while your `deny` can always be stricter. The `.egress` file only ever writes the application's own rules — it
cannot add, change or remove an organization's.

On Osyrin Cloud an organization's Owners and Admins keep its **Allowed hosts** in the organization's settings. While
that list holds a host, every application of the organization reaches only the hosts on it — whatever the application
allows, and whatever a kit it uses asks for, a kit granted any host included. A change reaches every application at
its next call out, wherever it runs; an empty list hands the decision back to each application's own rules.
`osy egress list` shows the organization's hosts beside your own, marked as not yours to change, and a host the list
refuses is named in the refusal with where to add it.

Beside it the organization keeps its **Blocked hosts**: hosts none of its applications may reach. A blocked host is
refused whatever else allows it — the application's own rules, a kit granted any host, and the organization's own
allowed hosts. The application's code, every kit it uses and the page's browser code are all held to it, from the next
call out. The refusal says the organization blocks the host; only an Owner or Admin of the organization can unblock it.

## See also       {#see-also}
- [package.osy](https://osysharp.com/reference/project/package/) — granting a package the hosts it needs, `egress browser` for browser code
- [Hosting an Osy# app](https://osysharp.com/reference/project/hosting/) — where a deploy runs
- [Deploying while workflows are running](https://osysharp.com/reference/project/app-versions/) — what a deploy mints
