# Keep cards live from an outside address

> Canonical HTML: https://screenrig.ai/docs/sources

A data source is an address screenRIG reads on a schedule. It writes what it
finds into one card's data, so the card stays current without anyone
republishing, and every screen showing the card refetches it when it changes.
A Google, Outlook, iCloud or Nextcloud calendar becomes an agenda or a room
board.

## How a source works

- **One source, one card.** A source names the card it fills. A card has at most
  one source, and a project has up to 200.
- **It runs on a schedule.** A calendar source syncs every 5 minutes, and every
  source can be set between 1 minute and 24 hours. A source runs within seconds
  of being created or changed, and `source refresh` syncs it now.
- **A failed sync never changes the card.** The card keeps its last good
  document, and the source's status says what went wrong. Failures retry by
  themselves, waiting longer after each one.
- **Only changes are written.** When a sync finds what the card already holds,
  nothing is written and screens are not woken.
- **Syncs are free.** Documents count toward card data storage like any other
  write.
- **Credentials are sealed.** An address that grants access is stored sealed,
  shown back only as its scheme and host, and never appears in a log, an error
  or the project's event log.

## Create a source

Create the card first, then preview, then save.

```sh
screenrig card create --type events --file events.json
screenrig source test --kind ics --card crd_CARD \
  --set 'calendars=[{"key":"lobby"}]' --set time_zone=America/Vancouver \
  --secret calendar_url.lobby=@lobby.url
screenrig source create --kind ics --card crd_CARD --name "Lobby calendar" \
  --set 'calendars=[{"key":"lobby"}]' --set time_zone=America/Vancouver \
  --secret calendar_url.lobby=@lobby.url
screenrig source show src_SOURCE
```

`source test` fetches the address, shapes it for the card and checks the result
against the card, and saves and writes nothing. It prints the document, whether
the card accepts it, and what it noticed along the way. Give the address in a
file (`@FILE`) or on stdin (`-`) so it stays out of your shell history.

| Command | What it does |
| --- | --- |
| `source list`, `source show ID` | The project's sources, with status, settings and masked secrets |
| `source create` | Saves a source and runs it within seconds |
| `source update ID` | Changes the name, card, settings (`--set KEY=VALUE`, or `--settings JSON`), secrets (`--secret`, `--unset-secret`), schedule (`--refresh 10m`) or `--enable` and `--disable` |
| `source test` | Previews a source, saved or not, without writing |
| `source refresh ID` | Syncs now and shows the result |
| `source delete ID` | Removes the source; the card keeps its last document |

`--settings` takes the source's whole config as JSON, `@FILE` or `-`. The
`--config` option of the CLI names its credential file and has nothing to do
with sources.

## Status and errors

Every source shows its status: when it last ran, last succeeded and last
changed the card, when it runs next, how many syncs in a row have failed, and
the last error.

| State | Meaning |
| --- | --- |
| `pending` | The source has not run yet |
| `ok` | The last fetch succeeded and the card accepted the document |
| `error` | The last sync failed; the card keeps its last good document |
| `disabled` | The schedule is stopped |

`ok` does not judge whether the data upstream is right or current, or whether a
screen shows the card.

| Error code | What happened |
| --- | --- |
| `unreachable` | screenRIG could not reach the address. Use a public http(s) address on port 80 or 443 |
| `timeout` | The address did not answer within 20 seconds |
| `too_large` | The answer is larger than 5 MB |
| `http_status` | The address answered with an HTTP error. A 404 means the address is no longer right |
| `auth_failed` | The address refused the credentials; the next try waits an hour |
| `parse_failed` | The answer is not a calendar. A sign-in page reads as a web page, not a calendar: use the calendar's ICS address |
| `rate_limited` | The service is limiting requests; the next sync waits |
| `card_data_invalid` | The card refused the document at the path the message names |
| `target_missing` | The card was deleted. Point the source at another card |
| `payment_required` | The project's storage is full, so the document was not written |
| `config_invalid` | A setting or stored secret can no longer be used. Enter it again |
| `internal` | The sync failed inside screenRIG and retries |

Creating or changing a source can answer `source_target_taken` (the card
already has a source), `source_target_unsupported` (the kind cannot fill that
card; the answer lists the cards it can), `source_secret_required` (a secret is
missing, or was entered for a different address and must be entered again),
`source_limit_reached` and `source_busy` (a sync is running; retry after the
interval). `source test` answers `source_test_failed` with the reason when the
address cannot be read.

## Calendars

A calendar source reads iCalendar (ICS) addresses: the secret address Google
Calendar, Outlook, iCloud and Nextcloud give for a calendar, and any
`webcal://` link. It fills an `events` card with one agenda of every calendar,
or a `study-rooms` card with one room per calendar.

| Setting | Meaning |
| --- | --- |
| `calendars` | 1 to 60 calendars, each `{key, name, floor, capacity}`. The key is lower-case letters, digits, `-` and `_`; name, floor and capacity label a room |
| `days` | How many days from today to show, 1 to 31 (default 7) |
| `time_zone` | An IANA zone such as `America/Vancouver`, used for the day boundary and for times a feed leaves unzoned |
| `private_title` | The title shown for private events (default `Private`) |

Each calendar has one secret, `calendar_url.KEY`, holding its address.

- **Recurring events** (daily, weekly, monthly and yearly rules, exceptions,
  moved and cancelled occurrences) and time zones are expanded on the server, so
  an event keeps its hour across daylight saving time and a card never repeats
  the calendar's own rules. Windows time zone names from Outlook are understood.
- **Private events** (`CLASS:PRIVATE` or `CONFIDENTIAL`) show their times only,
  with the private title and no organiser or location. The rule covers those
  marked private and does not judge what other events reveal.
- **Free time** (events marked transparent or free) is kept on an agenda and left
  out of a room board, where only busy time counts. Cancelled events are
  dropped, and tentative ones count as busy.
- **All-day events** show as dates; an event with no end shows no end.
- **Warnings.** When a feed names no time zone, uses one screenRIG cannot place,
  or holds more than 500 occurrences of one event or 5,000 events, the sync
  succeeds and the status lists a warning.
- **Several calendars** sync together: if one fails, the sync fails whole and the
  error names the calendar, so a board never shows a partial set.

<!-- sources:tabular -->

<!-- sources:rooms -->

<!-- sources:pos -->

## Security

An address that grants access is a credential. screenRIG stores it sealed,
shows back only its scheme and host, and uses it only to run the sync. Sources
read public http(s) addresses on port 80 or 443 and follow at most three
redirects; an address that does not resolve to a public host, a refused
connection and a failed handshake all read the same, so a source cannot be used
to explore a network. No message quotes an address or the answer it returned.
Sources only read: they never write to a calendar.

## Related

- [Cards](https://screenrig.ai/docs/cards.md): card types, properties and live data.
- [Signage CLI](https://screenrig.ai/docs/cli.md): every `screenrig` command.
- [API reference](https://screenrig.ai/docs/api.md): the `/api/sources` routes and their problems.
