Skip to content

Furry schedule adapter ​

The adapter reads one edition from the API and serves that edition's schedule as a Furry Schedule Schema document. Run it if something you use already consumes that format, such as a convention app or a site built around it. If you are writing the consumer yourself, read the schedule from the API directly instead; Showing the schedule covers that.

It is a separate program with its own image, chart and version line, and it only reads: nothing it does changes anything in Eventail.

Before you start ​

  • A running Eventail 0.2.0 or newer the adapter can reach. It is usually on the same network and not exposed.
  • A client credentials client at your sign-in provider, set up as described in Sign-in provider. The adapter signs in as itself, the same way any integration does.
  • The ID of the edition to publish. Its settings page in the web app shows it, with a button that copies it.

Docker Compose ​

The Compose setup ships the adapter behind a profile, so it stays out of the way until you ask for it. Fetch its env file next to the others:

sh
base=https://raw.githubusercontent.com/eventail-scheduling/eventail-deploy/main/compose
curl -fsSLO "$base/furry-schedule-adapter.env.example"
cp furry-schedule-adapter.env.example furry-schedule-adapter.env

Fill in the edition and the client, then start it alongside the rest:

sh
docker compose --profile furry-schedule-adapter up -d

The document is published on 127.0.0.1:8081 by default, which FURRY_SCHEDULE_ADAPTER_PORT in .env changes. Put it behind the same reverse proxy that terminates TLS for the API and the web app.

compose.yml sets the API's address and the token cache path itself, because both follow from the Compose setup rather than from your deployment.

Kubernetes ​

The adapter has its own chart, versioned separately from the Eventail chart. Create a Secret holding the client secret, then install:

sh
kubectl create secret generic furry-schedule-adapter --namespace eventail \
    --from-literal=clientSecret=...

helm install furry-schedule-adapter \
    oci://ghcr.io/eventail-scheduling/charts/eventail-furry-schedule-adapter \
    --namespace eventail --values values.yaml

A minimal values.yaml:

yaml
eventail:
  baseUrl: http://eventail-api
  editionId: 00000000-0000-0000-0000-000000000000
  auth:
    issuer: https://id.example.com/realms/eventail
    clientId: furry-schedule-adapter
    existingSecret: furry-schedule-adapter

document:
  language: en

Nothing is exposed by default. To publish the document, set the hostnames and enable whichever router your cluster uses:

yaml
hostnames:
  - schedule.example.com

ingress:
  enabled: true
  className: nginx
  tls:
    - secretName: schedule-tls
      hosts:
        - schedule.example.com

With Gateway API, name the Gateway to attach to instead:

yaml
hostnames:
  - schedule.example.com

httpRoute:
  enabled: true
  parentRefs:
    - name: public
      namespace: gateways

The Gateway holds the listeners and their certificates, so TLS is configured there rather than in these values. Both routers may be enabled at once while you move between them.

The access token is cached on a PersistentVolumeClaim so a restart does not mint another one. Providers meter these, and an emptyDir would lose the cache every time the pod restarts.

A plaintext auth.issuer is refused unless it is on loopback or you set eventail.auth.allowInsecureIssuer, which is for a provider inside the cluster with no TLS to terminate. It also drops the HTTPS requirement for the request that carries the client secret.

What it serves ​

The document is at /schedule.json. The adapter fetches the schedule at startup and polls for changes after that, so each response is a complete snapshot of what is published.

Until the first poll succeeds, /schedule.json answers 503 rather than an empty document, and it does so again if the schedule goes too stale to serve. Both responses say why. /health answers 200 whenever the process is running, stale or not, so an upstream outage does not restart the container.

Rooms carry the venue they sit in, taken from the edition, so a convention running in two buildings comes out as two venues. An Eventail older than 0.2.0 does not offer the venue include at all, so it refuses every request the adapter makes and /schedule.json keeps answering 503.

Membership levels are off until you name the question that holds them. Ask sessions a choice question whose options are the levels you offer, then set document.membershipCustomFieldKey to its external key. The options become the document's membership levels as soon as a session has answered, and each session's answer says which ones its event is open to. A session that answers nothing is open to everyone.

Configuration ​

Set each one as an environment variable or in a file the container reads, /app/config/local.toml or /app/config/local.json. Environment variables override the file. An environment variable's name is the setting's path in upper case, with underscores between words and between path parts: eventail.auth.clientId becomes EVENTAIL_AUTH_CLIENT_ID. An empty variable still counts as set. The Setting column shows the variable's name on its second line.

Durations use ISO 8601 notation, such as PT30S or P7D. Intervals accept time units only: PT24H works, P1D does not.

general​

SettingTypeDefaultDescription
port
PORT
integer, 1 to 655353000The port the document is served on.

log​

Logging to standard output: JSON lines when NODE_ENV is production, readable text otherwise.

SettingTypeDefaultDescription
log.level
LOG_LEVEL
one of trace, debug, info, warn, error, fatalinfo

eventail​

Where the schedule is read from.

SettingTypeDefaultDescription
eventail.baseUrl
EVENTAIL_BASE_URL
URLRequiredThe root of the eventail API. A trailing slash is ignored. The access token is sent here, so reach the API over https or over a network you trust. Plaintext is not refused, because an in-cluster address is the normal case and no rule separates one from a public host.
eventail.editionId
EVENTAIL_EDITION_ID
stringRequiredThe edition whose schedule is published.
eventail.pollInterval
EVENTAIL_POLL_INTERVAL
ISO 8601 durationHow often the published schedule is checked for a new revision outside the convention itself. An ISO 8601 duration in time units only.
Examples: PT5M, PT1H
eventail.livePollInterval
EVENTAIL_LIVE_POLL_INTERVAL
ISO 8601 durationHow often it is checked from the day before the convention opens until it closes, when a schedule moves far more than it does the rest of the year. An ISO 8601 duration in time units only.
Examples: PT30S
eventail.requestTimeout
EVENTAIL_REQUEST_TIMEOUT
ISO 8601 durationHow long a single request may take, to the eventail API and to the identity provider alike. An ISO 8601 duration in time units only.
Examples: PT10S

eventail.auth​

The client credentials the adapter authenticates with. The API must accept the resulting token as an integration; any other kind of caller reads more, not less, and would publish unconfirmed sessions.

SettingTypeDefaultDescription
eventail.auth.issuer
EVENTAIL_AUTH_ISSUER
URLRequiredThe OpenID Connect issuer to mint access tokens from. Its discovery document is fetched at startup, so the adapter does not start while the issuer is unreachable, and the document must name this same issuer back.
eventail.auth.clientId
EVENTAIL_AUTH_CLIENT_ID
stringRequiredThe client credentials client the adapter authenticates as.
eventail.auth.clientSecret
EVENTAIL_AUTH_CLIENT_SECRET
stringRequiredSupply this through the environment rather than a config file: EVENTAIL_AUTH_CLIENT_SECRET. Anyone holding it can mint tokens with the adapter's access.
eventail.auth.scope
EVENTAIL_AUTH_SCOPE
stringSent with the client credentials request when a provider needs one to issue a token carrying the right claims.
eventail.auth.audience
EVENTAIL_AUTH_AUDIENCE
stringSent with the client credentials request. Auth0 needs it to issue a token for the API; a provider that maps the audience on the client instead, as Keycloak does, needs it left out. Either way it has to end up matching the API's jwt.audience.
eventail.auth.allowInsecureIssuer
EVENTAIL_AUTH_ALLOW_INSECURE_ISSUER
booleanfalseAccept a plaintext issuer that is not on loopback. Needed only for a provider reached inside a cluster or a compose network, where there is no TLS to terminate. It also tells openid-client to drop its HTTPS rule for every request to that provider, including the one carrying the client secret.
eventail.auth.tokenCachePath
EVENTAIL_AUTH_TOKEN_CACHE_PATH
stringA file to hold the access token between restarts, created 0600. Left out, the token lives only in memory and every start mints a new one, which some providers meter. The entry is keyed by the credentials, so editing other settings reuses it. The adapter refuses to start if it cannot write here.
Examples: /var/cache/adapter/token.json

document​

What the emitted document says.

SettingTypeDefaultDescription
document.language
DOCUMENT_LANGUAGE
stringRequiredThe language tag every localized string is keyed by. eventail stores one language per edition without naming it, so the tag is stated here. Case is normalized. A tag beyond a language and a region is accepted but warned about, since a consumer matching the pattern the schema names would not find the text.
Examples: en, de, en-GB
document.descriptionSource
DOCUMENT_DESCRIPTION_SOURCE
one of abstract, descriptionabstractWhich session field becomes the event description. Both are optional per edition, so an edition that collects only one has to name it.
document.membershipCustomFieldKey
DOCUMENT_MEMBERSHIP_CUSTOM_FIELD_KEY
stringThe external key of the choice question asked of sessions whose options are the membership levels, and whose answer says which ones an event is open to. Single and multiple choice both work. Left out, the document names no membership levels and restricts no event, and a key no session question carries does the same.
Examples: membership
document.maxStaleness
DOCUMENT_MAX_STALENESS
ISO 8601 durationHow long the last document keeps being served once refreshing it stops working, measured from the last refresh that succeeded rather than the last attempted. Past it the document is refused rather than served stale, which is the only signal a consumer gets. An ISO 8601 duration in time units only.
Examples: PT6H, PT24H

source​

Identifies this publisher to whoever consumes the document.

SettingTypeDefaultDescription
source.name
SOURCE_NAME
stringeventail
source.vendorId
SOURCE_VENDOR_ID
string