Skip to main content

External services

Out of the box, the bundle runs everything itself. You can replace the bundled object storage and the bundled login server with your own, connect an email server, and switch on optional integrations. All of this is configured in .env; apply every change with:

docker compose up -d

The bundled components are chosen with COMPOSE_PROFILES:

ProfileComponent
garageBundled S3 object storage. Remove it to use your own S3.
keycloakBundled login server. Remove it to use your own Keycloak.
backupNightly backups (see Backups).

The default is COMPOSE_PROFILES=garage,keycloak.

Your own S3 storage​

GOAT stores uploaded files in an S3 bucket, and avatars and images in a second, publicly readable bucket. Any S3-compatible storage works.

Decide before the first start

Set up your own storage before you start GOAT for the first time. Data already in the bundled Garage is not moved to your storage.

1. Prepare the buckets​

  • Uploads bucket (S3_BUCKET_NAME, default goat-uploads). Browsers upload files straight to this bucket, so its CORS rules must allow PUT with a Content-Type header from the GOAT URL. The bundled Garage uses this rule, which you can adapt:

    [
    {
    "AllowedOrigins": ["https://goat.example.org"],
    "AllowedMethods": ["GET", "HEAD", "PUT"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
    }
    ]
  • Assets bucket (ASSETS_BUCKET_NAME, default goat-assets) for avatars and images. It must be readable without credentials at the address you set as ASSETS_URL. If your storage allows no publicly readable bucket, skip it: set ASSETS_SERVED_BY_CORE=true and ASSETS_URL=<GOAT_PUBLIC_URL>/api/v2/asset/file. GOAT then keeps the images in the uploads bucket under assets/ and serves them itself.

Both buckets are accessed with the same access key.

2. Configure .env​

Remove garage from COMPOSE_PROFILES, then set:

SettingMeaning
S3_ENDPOINT_URLThe S3 endpoint as the server reaches it, e.g. https://s3.example.org
S3_PUBLIC_ENDPOINT_URLThe S3 endpoint as browsers reach it; upload links point here
S3_REGIONThe region, e.g. eu-central-1
S3_BUCKET_NAMEThe uploads bucket
S3_FORCE_PATH_STYLEtrue for path-style addresses (endpoint/bucket/key), false for virtual-hosted style (bucket.endpoint/key)
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYYour access key. Replace the values that setup.sh generated for Garage.
ASSETS_S3_ENDPOINT_URLEndpoint of the assets bucket; defaults to S3_ENDPOINT_URL
ASSETS_BUCKET_NAMEThe assets bucket
ASSETS_URLPublic address of the assets bucket, e.g. https://assets.example.org
ASSETS_SERVED_BY_COREtrue keeps the images in the uploads bucket and lets GOAT serve them, for storage without a public bucket

GARAGE_RPC_SECRET and GARAGE_ADMIN_TOKEN are not used without the bundled Garage.

Your own Keycloak​

GOAT can use a Keycloak you already run instead of the bundled one.

Client requirements​

Create a client for GOAT in your realm with these settings:

SettingValue
Client typeConfidential (client authentication on), with the standard flow enabled
Valid redirect URIs<GOAT URL>/*, e.g. https://goat.example.org/*
Web origins<GOAT URL>
Service accountEnabled, with the realm-management roles view-users and manage-users

GOAT uses the service account to read user accounts, to keep a user's name and email in Keycloak in step with their GOAT profile, and to delete an account when its user deletes it in GOAT.

In the default mode of the AI assistant, the realm must have Revoke Refresh Token off (Realm settings, Tokens), so that the assistant can renew logins during long answers; see AI assistant.

Configure .env​

Remove keycloak from COMPOSE_PROFILES, then set:

SettingMeaning
KEYCLOAK_PUBLIC_URLKeycloak as browsers reach it, e.g. https://login.example.org
KEYCLOAK_INTERNAL_URLKeycloak as the GOAT containers reach it; often the same URL
REALM_NAMEYour realm
KEYCLOAK_CLIENT_IDThe client ID
KEYCLOAK_CLIENT_SECRETThe client secret. Replace the value that setup.sh generated.

Both URLs include Keycloak's relative path if yours uses one, such as /auth: https://login.example.org/auth.

The first administrator and smoke.sh

With your own Keycloak, GOAT creates no user there. smoke.sh signs in with GOAT_ADMIN_EMAIL and GOAT_ADMIN_PASSWORD through a direct password login. To use it, set both to an existing user of your realm and enable Direct access grants on the client.

If your Keycloak uses a certificate from a private CA, see Company CA.

A realm shared with other applications​

Every client of a realm can get a token signed with the realm's key, and by default GOAT's APIs accept such a token even when Keycloak issued it to another application. A rule that lets only some users sign in to GOAT, such as a group check on GOAT's client, then guards the login page but not the APIs.

Set AUTH_ALLOWED_CLIENTS to the clients whose tokens GOAT accepts, comma-separated:

AUTH_ALLOWED_CLIENTS=goat

GOAT then checks each token's azp claim, the client Keycloak issued it to, and answers a token of any other client with 401. The list must include KEYCLOAK_CLIENT_ID: the web app signs users in with that client, and the print worker and GOAT's services pass those tokens on. Without it nobody could use GOAT, so the core container refuses to start and names both settings. Add any other client whose tokens should reach GOAT's APIs, such as one your own scripts sign in with. The check needs no change to the realm. Apply it with docker compose up -d.

What the check does not cover

The check is about the application a token was issued to, not about the user.

  • If only some users may use GOAT, for example the members of a group, enforce that on GOAT's client for every way it issues tokens: the browser login and, if enabled, Direct access grants (a direct password login, which Keycloak runs through its own flow). Otherwise turn Direct access grants off on the client. A user outside the group could else get a token of GOAT's client through the grant your rule does not cover, and GOAT's APIs would accept it. smoke.sh needs Direct access grants (see above).
  • The web app keeps users signed in with a refresh token, so a user removed from that group stays signed in until the session ends. To end it at once, sign the user out in Keycloak, including the user's offline sessions.

Email​

GOAT sends invitations, and Keycloak sends password resets, through one SMTP server. Email is off while SMTP_HOST is empty.

SettingMeaning
SMTP_HOST, SMTP_PORTThe mail server, e.g. smtp.example.org and 587
SMTP_SECURITYstarttls (usually port 587), ssl (usually port 465) or none (e.g. an internal relay on port 25)
SMTP_USER, SMTP_PASSWORDThe login. Leave both empty for a relay that accepts mail without one.
SMTP_FROMSender address. Defaults to SMTP_USER, so it is required for a relay without login.
EMAILS_FROM_NAMESender name, default GOAT

For example, a relay inside your network:

SMTP_HOST=mail.internal.example.org
SMTP_PORT=25
SMTP_SECURITY=none
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=goat@example.org

After changing SMTP_SECURITY, run ./setup.sh once: it derives the two flags SMTP_STARTTLS and SMTP_SSL that Keycloak needs. Then apply the change with docker compose up -d.

With starttls or ssl, GOAT and Keycloak check the relay's certificate against the public certificate authorities. If your relay's certificate comes from a company CA, set GOAT_CA_BUNDLE as described under Company CA; otherwise sending fails with a certificate error.

One place for the email settings

The bundled Keycloak uses the same SMTP settings: on every docker compose up -d, the step keycloak-sync copies them from .env into the realm. Change them in .env only; changes made in the Keycloak admin console under Realm settings β†’ Email are replaced on the next start.

Email branding​

By default, the emails from GOAT and from Keycloak show the name GOAT and no footer links. These optional settings add your own, in both:

SettingMeaning
EMAIL_BRAND_NAMEName shown in the emails
EMAIL_LOGO_URLAddress of a logo image, shown instead of the name
EMAIL_CONTACT_URLContact link in the footer. The app uses it too, for its Contact us links.
EMAIL_PRIVACY_URLPrivacy policy link in the footer. The login page links to it too.
EMAIL_COMPANY_NAMEWho runs your GOAT, as a footer line, e.g. your organization's name and address. Left out while empty.
EMAIL_COMPANY_URLWebsite the footer line links to.

The images in the emails come from GOAT's public URL (<public URL>/assets), or from STATIC_ASSETS_URL if you set it. Recipients' mail clients load them from there, so the address must be reachable for them.

Support tickets​

GOAT can hand problem reports and questions from its users to an Odoo Helpdesk, where they appear as tickets with replies. This is optional and off by default. Without it, Report a problem in the app shows an email address and the Documentation link, and the routes under /api/v2/support answer 404, except /api/v2/support/summary, which tells the app that support is off. The AI assistant's Feedback link opens the same address. It defaults to support@plan4better.de; set your own with NEXT_PUBLIC_SUPPORT_EMAIL, see the configuration reference.

SettingMeaning
ODOO_URLAddress of the Odoo, e.g. https://odoo.example.org. Shared by GOAT's Odoo integrations.
ODOO_DBThe Odoo database. Shared by GOAT's Odoo integrations.
ODOO_SUPPORT_API_KEYAPI key of the support user in that Odoo. This is a secret.
ODOO_SUPPORT_TEAM_IDNumeric ID of the Helpdesk team that receives the tickets
ODOO_SUPPORT_POST_ACTIONOptional. Name of the Odoo server action that posts a message as the ticket participant. Default GOAT: post message as ticket participant.

Support tickets are on only when the first four settings are all set; with any of them empty, they stay off. The API key is issued by Plan4Better for its own Odoo. Leave all four empty on your own installation unless you have received one.

Ticket messages can carry attachments of up to 55 MiB in one request. The bundled Caddy sets no limit on request bodies. If you put your own proxy or load balancer in front, it must accept bodies of at least 56 MB on /api/v2/support and allow generous read timeouts there.

Login off​

For local tests and demos, GOAT can run without any login:

AUTH=False

Everybody who opens GOAT then acts as one built-in administrator.

warning

With AUTH=False, anyone who can reach the URL has full access to all data. Use it only for local or demo installations.

AI assistant​

The assistant is part of every installation. It stays hidden until a language model is configured, in one of two ways:

  • GOAT AI for every organization. Set the built-in connection in .env, for example:

    ASSISTANT_DEFAULT_PROVIDER=openai_compatible
    ASSISTANT_DEFAULT_MODEL=<model ID>
    ASSISTANT_DEFAULT_BASE_URL=https://llm.example.org/v1
    ASSISTANT_DEFAULT_API_KEY=<key>

    Which of ASSISTANT_DEFAULT_API_KEY, ASSISTANT_DEFAULT_BASE_URL and ASSISTANT_DEFAULT_REGION a provider needs is listed in the configuration reference.

  • A connection of the organization's own. An organization admin adds it under Settings β†’ AI. The credentials are stored encrypted with ASSISTANT_SECRETS_KEY, which setup.sh creates.

Apply changes with docker compose up -d. Users then find Ask AI in the upper bar.

Amazon Bedrock and Google Vertex AI without an API key use their own credentials. Put them into a file assistant.env next to .env; only the assistant reads it:

# assistant.env
AWS_ACCESS_KEY_ID=<key id>
AWS_SECRET_ACCESS_KEY=<secret>
# or, for Vertex AI, a service account file in ./certs:
# GOOGLE_APPLICATION_CREDENTIALS=/certs/vertex.json

These credentials apply to GOAT AI only, never to organizations' own connections. The assistant runs as the user appuser in its container, so a key file in ./certs must be readable by it (for example chmod 644).

Long answers and Keycloak. An answer keeps running when the browser closes. When it outlasts the user's login, the assistant renews the login with the web app's Keycloak client, the one in KEYCLOAK_CLIENT_ID. Your own Keycloak's realm must therefore have Revoke Refresh Token off (Realm settings, Tokens); the bundled realm is set up that way.

With Keycloak 26.2 or later you can use token exchange instead (ASSISTANT_TOKEN_EXCHANGE_ENABLED=true). Create a confidential client for the assistant and enter it in ASSISTANT_KEYCLOAK_CLIENT_ID and ASSISTANT_KEYCLOAK_CLIENT_SECRET. Two settings are required, and without them every answer fails at its start:

  • On the assistant's client, switch on Allow refresh token in Standard Token Exchange.
  • On the web app's client, add an audience mapper that puts the assistant's client into the aud claim of the access token.

With token exchange, Revoke Refresh Token is not required, because the assistant gets a refresh token of its own. The bundled Keycloak is version 22 and does not support token exchange.

Company CA. The assistant trusts GOAT_CA_BUNDLE in addition to the public CAs, for model servers, GOAT's services and Keycloak, like the other services; see Company CA. No further setting is needed.

Model servers in your own network. An organization's connection may not reach private or internal addresses unless ASSISTANT_ALLOW_PRIVATE_ENDPOINTS=true. GOAT AI is configured by you and is not restricted.

External agents reach GOAT's MCP endpoint at <GOAT_PUBLIC_URL>/assistant/mcp with a GOAT login token. The endpoint answers requests for GOAT's host name only (GOAT_HOSTNAME), and the catalog's former address /catalog/mcp is no longer served. The records of tool calls made over MCP are kept for ASSISTANT_MCP_AUDIT_RETENTION_DAYS days.

Database. The assistant connects with GOAT's database user, which owns the database. It needs that, because GOAT's permission checks run with the caller's privileges and read many tables of GOAT. To check the privileges, run:

docker compose exec assistant python -m assistant.dbcheck

It prints ok or lists every missing privilege.

Data processing. User questions and, depending on the organization's data policy, data from GOAT go to the model provider of the connection in use. For GOAT AI that is the provider you configure; for an organization's own connection, the organization's provider. An organization's data policy is Restricted until one of its admins selects Standard under Settings β†’ AI: until then only field names, types, counts and statistics leave GOAT, never rows or geometries.

Optional integrations​

GOAT works without any of these. Each one switches on a feature that needs an outside service.

SettingWhat it enablesWithout it
NEXT_PUBLIC_MAPTILER_KEYThe satellite/hybrid basemap from MapTilerThat basemap is hidden; the other basemaps work
NEXT_PUBLIC_MAPBOX_TOKENPlace search in the map's search box (Mapbox)The search box finds no places
CATALOG_S3_BUCKET, CATALOG_S3_ENDPOINT_URL, CATALOG_S3_ACCESS_KEY_ID, CATALOG_S3_SECRET_ACCESS_KEY, CATALOG_S3_REGIONThe GOAT data catalog: a read-only bucket with the harmonised datasets, mirrored on a schedule. It also holds the OpenStreetMap and Overture Maps mirror (details)The catalog stays empty, its sync is switched off, and the OpenStreetMap and Overture Maps panes find no data
GEOCODING_URL, GEOCODING_AUTHORIZATIONThe geocoding service used by analysis toolsAnalysis steps that need geocoding cannot run
OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINTExport of traces, metrics and logs to your OpenTelemetry collectorNothing is exported

Links shown in the app and the source of the product artwork are listed in the configuration reference. The source of the routing base data is described under Routing base data.

OpenStreetMap and Overture Maps​

The OpenStreetMap and Overture Maps panes under Add layer β€Ί Fetch / Connect data copy the features of an area into a GOAT layer. They read from a mirror of both datasets, stored as GeoParquet in the catalog bucket (CATALOG_S3_*) under mirror/osm and mirror/overture. The mirror is not part of a GOAT installation; it is published there by a separate pipeline, next to the catalog. Two parts of GOAT read it: processes fills the attribute and value lists in the dialog, and the Windmill tools worker runs the copy. Nothing else needs setting: without a catalog bucket, the two panes find no data.

The licence is recorded on each new layer: ODbL 1.0 for OpenStreetMap, CDLA-Permissive 2.0 for Overture places and ODbL 1.0 for the other Overture types.