Configuration
Setting up the bridge means creating a Slack app, collecting its two tokens, and writing a short TOML file.
Prerequisites
- A Screenshotter server reachable from where the bridge runs, with
require_auth_to_view = false. - Permission to create a Slack app in your workspace.
- Outbound HTTPS from the bridge host to
*.slack.com.
Create the Slack app
At https://api.slack.com/apps choose Create New App → From a manifest, pick your workspace, and paste the following, replacing the domain with the one your screenshot links use:
display_information:
name: Screenshotter
features:
bot_user:
display_name: screenshotter
always_online: true
unfurl_domains:
- screen.corp.example
oauth_config:
scopes:
bot:
- links:read
- links:write
- remote_files:write
- remote_files:read
# Only with the matching user restriction in the bridge config:
# - users:read # block_external_users
# - usergroups:read # allowed_usergroups
settings:
event_subscriptions:
bot_events:
- link_shared
socket_mode_enabled: true
org_deploy_enabled: falseThe same result can be reached through the UI: enable Socket Mode, add the
bot scopes above under OAuth & Permissions, subscribe to the link_shared
bot event and register the unfurl domains under Event Subscriptions.
Two steps remain in both cases:
- Basic Information → App-Level Tokens → Generate Token and Scopes: add
the
connections:writescope and copy thexapp-…token. This is theapp_token. - OAuth & Permissions → Install to Workspace, then copy the Bot User
OAuth Token (
xoxb-…). This is thebot_token.
The App ID, Client ID, Client Secret and Signing Secret shown on the app’s pages are not used: with Socket Mode there is no inbound request to verify.
Adding a scope to an installed app only takes effect after Reinstall to Workspace on the OAuth & Permissions page.
Unfurl domains
Slack only reports links with an explicit http:// or https:// scheme whose
host is one of the app’s unfurl domains, and the bridge additionally checks
the host against its own unfurl_domains. The two lists must match. The
domain does not have to be resolvable from anywhere: the link only supplies
the image ID, and the image itself is fetched from screenshotter_base_url
(or screenshotter_fetch_base_url, when set).
A bare intranet name such as http://screen/abcdef works.
On a matching domain, any single-segment path is taken as an image ID (with an
optional .png suffix), except the server’s own routes (upload, assets,
static, thumb, admin, auth, favicon.ico).
Configuration file
Create config.toml. The minimum is:
screenshotter_base_url = "http://screenshotter.internal:8080"
unfurl_domains = ["screen.corp.example"]
bot_token_env_var = "SLACK_BOT_TOKEN"
app_token_env_var = "SLACK_APP_TOKEN"Prefer the *_env_var forms, which keep the tokens out of the file; each
token can be given either inline or through the environment, not both.
screenshotter_base_url(required): address of the server, without a trailing slash. The bridge fetches images from it and the card links to it, so a private address is fine as long as the bridge and the readers can reach it.screenshotter_fetch_base_url: address the bridge itself uses to reach the server, without a trailing slash, when it differs from the one readers click through to — a Kubernetes Service, for instance, withscreenshotter_base_urlleft as the address browsers open. Default: unset, i.e. fetch fromscreenshotter_base_url.unfurl_domains(required): hostnames to render; must match the Slack app’s unfurl domains.bot_token,app_token: thexoxb-…andxapp-…tokens; preferbot_token_env_varandapp_token_env_var.max_dimension: longest side of the preview image, in pixels. Default:1600;0sends the full resolution. Must be0or at least300.preview_wait: how long to wait for Slack to process the preview before showing the card. Default:8s. With0sthe card appears immediately and the image fills in a few seconds later; theremote_files:readscope is then unnecessary.request_timeout: timeout of each image fetch and Slack API call. Default:30s.max_concurrency: links handled in parallel; further events are dropped rather than queued. Default:50.max_image_workers: images decoded and resized in parallel, which bounds memory use. Default:4.max_links_per_message: links rendered from a single message. Default:5, which is also Slack’s own limit.block_external_users,allowed_usergroups: see below.excluded_paths: extra first path segments never treated as image IDs, for a server with custom routes.debug: verbose Socket Mode and API logging. Default:false. The output can include the bot token.
Preview images
The preview is a downscaled copy capped at max_dimension; readers get the
full-resolution screenshot by clicking through. Slack rejects previews under
300 pixels on a side, so smaller screenshots are enlarged and padded with
white rather than refused.
User restrictions
By default the bridge renders a recognised link for any user who can post it. Two optional settings narrow that:
block_external_users = trueignores links posted by users external to the workspace: Slack Connect participants, users from another team, and guest accounts. Requires theusers:readscope.allowed_usergroups = ["S0123ABCD"]only renders links posted by members of the listed user group IDs. Requires theusergroups:readscope.
Both fail closed: when Slack cannot be queried to evaluate the rule, the link is left alone. Lookups are cached for five minutes, so a membership change can take that long to apply.