# Serving Services from Workspaces

> Cordium documentation. Canonical page: <https://octelium.com/docs/cordium/latest/examples/security/serving>.

Besides accessing Octelium *Services*, a *Workspace* can serve them, i.e. act as the upstream of an Octelium *Service* through its own connection to the *Cluster* (read more [here](https://octelium.com/docs/cordium/latest/workspaces/secretless.md#serving-services)). The application running inside the *Workspace* then becomes a full-fledged Octelium *Service*, with its own address, *Policies*, access logs and, optionally, public or anonymous access, without opening any port or exposing the *Workspace* to any network. This example shows three use cases: a branch preview for the whole company, a webhook receiver for a third-party service, and a database shared with a teammate.

In all the cases, a *Service* is served by a specific *User* via the `user` field of its upstream, and the *Workspaces* of that *User* that are configured to serve it become its upstream. If several of them serve the same *Service*, the requests are load-balanced among them.

## A Branch Preview

The following `WEB` *Service* is served by `alice` and is public, which means that any authorized *User* can open it at `https://storefront-preview.<DOMAIN>` after logging in with the *Cluster*'s identity providers (read more about public access [here](https://octelium.com/docs/octelium/latest/management/core/service/clientless.md)):

```yaml
kind: Service
metadata:
  name: storefront-preview
spec:
  mode: WEB
  isPublic: true
  config:
    upstream:
      url: http://localhost:5173
      user: alice
  authorization:
    inlinePolicies:
      - spec:
          rules:
            - effect: ALLOW
              condition:
                match: '"employees" in ctx.user.spec.groups'
```

Alice then serves it from the *Workspace* that runs her branch's dev server. Since the dev server already runs as a background task (see the [Full-Stack Development](https://octelium.com/docs/cordium/latest/examples/dev/fullstack.md) example), she only needs to add the *Service* to the *Workspace*'s spec, from the **Config** tab of the web portal or via the API, and restart it:

```yaml
spec:
  runtime:
    octelium:
      serveServices:
        - storefront-preview
```

Or, when creating a new *Workspace*:

```bash
cordium create ws --template fullstack.storefront.cordium --port web:5173:default \
  --serve storefront-preview --start
```

Unlike application sharing, which is restricted to the *Users* of the *Cluster* via the portal (read more [here](https://octelium.com/docs/cordium/latest/workspaces/applications.md#sharing-applications)), the preview now has its own stable URL that does not depend on the *Workspace*'s name, can be protected by any *Policy*, and can be served by a different *Workspace* tomorrow without changing the URL.

## A Webhook Receiver

Third-party services such as Stripe, GitHub or Slack need a public URL to send their webhooks to, which is notoriously painful during development. The following `HTTP` *Service* is public and anonymous, i.e. reachable from the internet without any authentication (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/anonymous-access.md)):

```yaml
kind: Service
metadata:
  name: payments-webhooks-dev
spec:
  mode: HTTP
  isPublic: true
  isAnonymous: true
  config:
    upstream:
      url: http://localhost:8080
      user: alice
```

With `serveServices: ["payments-webhooks-dev"]` in her API *Workspace*, Alice configures `https://payments-webhooks-dev.<DOMAIN>/webhooks/stripe` as the webhook endpoint in Stripe's test mode dashboard, and the webhooks are delivered to `http://localhost:8080/webhooks/stripe` inside her *Workspace*. Every delivery appears in Octelium's access logs.

> **Note:**
>
> Anonymous *Services* are reachable by anyone on the internet. Only use them for endpoints that authenticate their requests themselves, as webhook receivers do with signatures, and stop serving them when you no longer need them.

## Sharing a Database with a Teammate

Suppose that Alice prepared a PostgreSQL database inside her *Workspace* (e.g. via Podman, as in [this example](https://octelium.com/docs/cordium/latest/examples/dev/containers.md)) with a dataset that reproduces a bug, and that Bob needs to query it. Instead of exporting and copying the data, she serves the database as a `POSTGRES` *Service* that only Bob can access:

```yaml
kind: Service
metadata:
  name: alice-repro-db
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://localhost:5432
      user: alice
    postgres:
      user: postgres
      database: payments
      auth:
        password:
          fromSecret: alice-repro-db-password
  authorization:
    inlinePolicies:
      - spec:
          rules:
            - effect: ALLOW
              condition:
                match: ctx.user.metadata.name == "bob"
```

Once Alice's *Workspace* serves `alice-repro-db`, Bob can query it from his laptop or from any of his *Workspaces* via `psql -h alice-repro-db`, without knowing the password, and Alice can revoke his access at any time by changing the *Policy* or by stopping serving it.
