# swb share

Sets one of the signed-in tenant's own services on the share ladder — who
sees it — and, for a service something binds to, who may bind it and whether
a first bind waits for the owner — the act of an app's **Sharing** tab.
[`swb service`](/command-line/service) shows the service without changing
it.

## Synopsis

```
usage: swb share <app> <env> <kind> <name> <level> [--read <tenant,...>|org]
                 [--maintain <tenant,...>] [--approval none|owner] [--json]
```

## Arguments and flags

| Argument or flag | Takes | Default |
|---|---|---|
| `<app>` | The app the service belongs to. Required. | — |
| `<env>` | The environment it runs in. Required. | — |
| `<kind>` | One of `app`, `api`, `dev-portal`, `site`, `agent` or `data`, in any case. Required. | — |
| `<name>` | Its name within that app and environment. Required. | — |
| `<level>` | `hidden`, `tenant`, `org` or `portal`, in any case — who sees it (below). Required. | — |
| `--read` | Tenant **ids**, comma-separated: the tenants that may bind it to read. `--read org` is every tenant; `--read ""` names none. | the audience as it stands, or the raise's (below) |
| `--maintain` | Tenant **ids**, comma-separated: the tenants that may bind it to maintain. `--maintain ""` names none. | the audience as it stands, or the raise's (below) |
| `--approval` | `none` or `owner`: whether a first maintain bind waits for the owner's approval. | as it stands |
| `--json` | Nothing. Prints the service as the API answered it instead of the summary. | off |
| `--help`, `-h` | Prints the command's help and exits. | off |

The levels, lowest first:

| Level | Who sees the service |
|---|---|
| `hidden` | No one: it runs and reads back, and appears nowhere. |
| `tenant` | This tenant's **Services** menu. |
| `org` | Every tenant's **Services** menu and the portal's internal views. |
| `portal` | The curated portal entry. |

`--read`, `--maintain` and `--approval` are for an `api`, `agent` or `data`
service, which something binds to. Who may open an `app`, a `site` or a
`dev-portal` is its Application's access policy, set on the app's **Access**
tab, so those take a level alone.

## What it does

Calls `Mutation.shareService` with the level, and the audience and the
approval where given. Where one of `--read` and `--maintain` is given and the
other is not, it first reads the service with `Query.service` and sends the
mode not given as it stands, since the API takes both together.

Where neither is given, no audience is sent, and the API decides it: a raise
widens who may read to the level's default — the owner added for `tenant`,
every tenant for `org` and `portal` — and gives maintain the owner where it
names nobody. A level that stays where it is or is lowered leaves the
audience as it stands.

Lowering a level narrows no audience and touches no binding: the service
leaves the menus and the portal above its new level, while who may bind it,
and every binding already made, stay as they are. Revoking a binding is its
own act. A share writes nothing but the service's level, audience and
approval.

## What it prints

The service the control plane recorded, as
[`swb service`](/command-line/service) prints it:

```
atlas/prod/data/ia
url          https://data.atlas.example/ia
share        org
approval     owner
fingerprint  sha256:3f9a
tenant       weidt-group
read         every tenant
maintain     weidt-group
```

`--json` prints the service as the API answered it: `app`, `env`, `kind`,
`name`, `url`, `tenant`, `share`, `approval`, `fingerprint`, and `audience`
with its `read` and `maintain`, a read audience of every tenant answered as
`["org"]`.

## The operation and the permission

`Mutation.shareService`. The caller needs `swb.publishing.write`, and only the
tenant that owns the service may change it. `swb share` also reads
`Query.me` once for the tenant, and `Query.service` where one mode of the
audience is given alone.

## The same act in the console

An app's page → **Sharing** → the service's **Actions** → **Edit sharing…** →
**Save sharing**, with the tenants picked by name.

## Refusals and exit status

Exit status is 0 once the share is recorded, and 1 on every refusal below.

| Refusal | When |
|---|---|
| `swb share takes a service's <app> <env> <kind> <name> and a <level>` | Fewer than the five were given. |
| `<kind> takes app, api, dev-portal, site, agent, data, not "<value>"` | The kind is none of those. |
| `<level> takes hidden, tenant, org, portal, not "<value>"` | The level is none of those. |
| `--approval takes none, owner, not "<value>"` | `--approval` was given something else. |
| `the tenant holds no service <app>/<env>/<kind>/<name>` | One mode was given alone and the signed-in tenant holds no such service. |
| `not signed in or token expired — run swb login` | There is no usable credential. |
| The API's error, with its type | The control plane refused the write: `NotFound` for a service the tenant holds no row of, `NotAuthorized` for a caller without `swb.publishing.write` or a service another tenant owns, `ValidationFailed` for an audience or approval given to an `app`, `site` or `dev-portal`, `org` beside a tenant or as a maintainer, or a tenant that does not exist, and `Conflict` when the service's sharing changed while the share was in flight. |

## Example

```console
$ swb share breadboard dev app breadboard tenant
breadboard/dev/app/breadboard
url          https://breadboard.dev.swb.example
share        tenant
approval     none
fingerprint  9c2e…
tenant       weidt-group
read         weidt-group
maintain     weidt-group
```
