Ctrl K
ComunidadGuías…

DEPLOYMENT.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Self-hosting Assemblash

Assemblash is local-first. Run it on your machine. It needs no configuration,
no accounts, and no network. This page is about the other case: you serve it
to more than the machine it runs on.

## The rule

**The default is `127.0.0.1` with no token.** Anyone who can reach that socket
is already on the machine. There, the projects are ordinary readable files, so
there is nothing to gate.

**A bind to any other address refuses to start without an access token.** This
is not a warning. It is a refusal. A server that binds a network and continues
to serve publishes your workspace to that network. The flag would not look
like it was going to do that.

```sh
assemblash serve --bind 0.0.0.0
# error: refusing to bind 0.0.0.0: it is not a loopback address, and serving a
# network without an access token would publish this workspace to it.
# Run `assemblash token rotate` to create one, then start again
```

## Getting a token

```sh
assemblash token show     # prints it, creating one if there is none
assemblash token rotate   # replaces it; every existing client stops working
assemblash token clear    # removes it, so only a loopback bind will start
```

The token lives in the workspace's `config.toml` and nowhere else. There is
deliberately **no `--token` argument** anywhere. A secret on a command line is
a secret in your shell history and in every process listing on the machine.

## Using it

Every request carries the token as a bearer header:

```sh
curl -H "Authorization: Bearer $(assemblash token show)" \
     http://your-host:8787/api/projects
```

In a browser, open the server. It asks for the token once. The browser keeps
it in that tab (`sessionStorage`) and sends it as a header — **never in a
URL**. Then it cannot end up in browser history, in a `Referer`, or in a proxy
log.

A request without the token, or with a wrong token, gets a `401` in the same
typed error envelope as everything else:

```json
{ "error": { "code": "unauthorized",
             "message": "this server requires an access token: send it as `Authorization: Bearer <token>`",
             "details": {} } }
```

The comparison is constant time. The server never echoes back a rejected
token.

## Docker

The image binds `0.0.0.0` inside the container. A published port only reaches
a process bound to a routable address. The image therefore needs a token —
this is the safety property, not an obstacle:

```sh
docker compose run --rm assemblash token show
docker compose up
```

`compose.yaml` publishes on `127.0.0.1:8787` by default. Read the next
section. Then change it to `8787:8787` to reach the server from the network.

## TLS, and what the token is not

**The token authenticates. It does not encrypt.** It travels in a header.
Anyone who can read the traffic can read the token, and then use it.

For anything beyond a trusted network, put a reverse proxy in front, and let
the proxy terminate TLS. Assemblash speaks plain HTTP on purpose. Certificate
handling, renewal, and identity providers are large problems. Software that
does only this solves them well.

### Caddy

```caddyfile
assemblash.example.com {
    reverse_proxy assemblash:8787
}
```

Caddy obtains and renews a certificate itself. That is the whole file.

### Traefik

```yaml
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.assemblash.rule=Host(`assemblash.example.com`)"
  - "traefik.http.routers.assemblash.tls.certresolver=letsencrypt"
  - "traefik.http.services.assemblash.loadbalancer.server.port=8787"
```

### nginx

```nginx
server {
    listen 443 ssl;
    server_name assemblash.example.com;

    # Exports and asset uploads are files; the default 1 MB body limit is
    # smaller than a photograph.
    client_max_body_size 64m;

    location / {
        proxy_pass http://127.0.0.1:8787;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

From 1.9.0, a loopback server with **no token** refuses a `Host` that is not
`localhost`, `127.0.0.1`, or `::1`. This protects it from DNS rebinding. With
the example above, nginx sends `Host: assemblash.example.com`. Do one of these:

- Keep the token, as the checklist below says. A server with a token does not
  check `Host`.
- Or add the public name to `config.toml`:

  ```toml
  allowed-hosts = ["assemblash.example.com"]
  ```

### Identity providers

Assemblash has no accounts and no built-in OIDC. If you need per-person
identity, single sign-on, or an audit trail tied to real users, put it in the
proxy. `oauth2-proxy`, Authelia, and Traefik's forward-auth all do this. Keep
the token as the proxy's own credential to the backend.

## A short checklist

- [ ] Run `assemblash token show`. Keep the token somewhere sensible.
- [ ] Bind explicitly: `--bind 0.0.0.0`, or `bind` in `config.toml`.
- [ ] Publish through a reverse proxy with TLS if the server is reachable
      beyond a network you trust.
- [ ] Run `assemblash token rotate` if the token is ever pasted somewhere it
      should not have been. Every client will need the new token. That is the
      point.
- [ ] Back up the workspace directory. It is plain files: `document.json`,
      `assets/`, `history/`. Nothing is hidden in a database.