matrix-org / matrix-org/matrix-hookshot
Unable to access Hookshot Configuration widget due to 504 error.
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 450
- Forks
- 95
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 23
Description
I want to add `webhooks` from multiple apps to my `matrix` server (`synapse`), which I got working, but those apps send `json` data and have no option to change the data type. Looking around I found out about `allowJsTransformationFunctions`, so I set it to `true` but I couldn't understand how it works. Then I found that I can add `widgets` which makes these transformations easier but I when add the widget to the room and try to access I get error,

from the `docker` logs I see this,
```
hookshot | DEBUG 10:10:15:969 [MatrixHostResolver] No well-known found for MY_DOMAIN: Well known request returned non-200
hookshot | DEBUG 10:10:16:583 [MatrixHostResolver] No SRV found for MY_DOMAIN: querySrv ENOTFOUND _matrix._tcp.MY_DOMAIN
hookshot | DEBUG 10:10:16:583 [MatrixHostResolver] No result cached for MY_DOMAIN, caching result for 86400000ms:
hookshot | WARN 16:29:37:920 [ProvisioningApi] Failed to exchange the token for MY_DOMAIN AxiosError: connect ETIMEDOUT PUBLIC_IP:8448
hookshot | at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16) {
hookshot | port: 8448,
hookshot | address: 'PUBLIC_IP',
hookshot | syscall: 'connect',
hookshot | code: 'ETIMEDOUT',
hookshot | errno: -110,
hookshot | config: {
hookshot | transitional: {
hookshot | silentJSONParsing: true,
hookshot | forcedJSONParsing: true,
hookshot | clarifyTimeoutError: false
hookshot | },
hookshot | adapter: [Function: httpAdapter],
hookshot | transformRequest: [ [Function: transformRequest] ],
hookshot | transformResponse: [ [Function: transformResponse] ],
hookshot | timeout: 0,
hookshot | xsrfCookieName: 'XSRF-TOKEN',
hookshot | xsrfHeaderName: 'X-XSRF-TOKEN',
hookshot | maxContentLength: -1,
hookshot | maxBodyLength: -1,
hookshot | env: { FormData: [Function] },
hookshot | validateStatus: [Function: validateStatus],
hookshot | headers: {
hookshot | Accept: 'application/json, text/plain, */*',
hookshot | Host: 'MY_DOMAIN',
hookshot | 'User-Agent': 'axios/0.27.2'
hookshot | },
hookshot | params: { access_token: 'SOME_TOKEN' },
hookshot | method: 'get',
hookshot | url: 'https://MY_DOMAIN:8448/_matrix/federation/v1/openid/userinfo',
hookshot | data: undefined
hookshot | },
hookshot | request: Writable {
hookshot | _events: {
hookshot | close: undefined,
hookshot | error: [Function: handleRequestError],
hookshot | prefinish: undefined,
hookshot | finish: undefined,
hookshot | drain: undefined,
hookshot | response: [Function: handleResponse],
hookshot | socket: [Function: handleRequestSocket]
hookshot | },
hookshot | _writableState: WritableState {
hookshot | highWaterMark: 16384,
hookshot | length: 0,
hookshot | corked: 0,
hookshot | onwrite: [Function: bound onwrite],
hookshot | writelen: 0,
hookshot | bufferedIndex: 0,
hookshot | pendingcb: 0,
hookshot | [Symbol(kState)]: 17580812,
hookshot | [Symbol(kBufferedValue)]: null
hookshot | },
hookshot | _maxListeners: undefined,
hookshot | _options: {
hookshot | maxRedirects: 21,
hookshot | maxBodyLength: 10485760,
hookshot | protocol: 'https:',
hookshot | path: '/_matrix/federation/v1/openid/userinfo?access_token=SOME_TOKEN',
hookshot | method: 'GET',
hookshot | headers: [Object],
hookshot | agent: undefined,
hookshot | agents: [Object],
hookshot | auth: undefined,
hookshot | hostname: 'MY_DOMAIN',
hookshot | port: '8448',
hookshot | nativeProtocols: [Object],
hookshot | pathname: '/_matrix/federation/v1/openid/userinfo',
hookshot | search: '?access_token=SOME_TOKEN'
hookshot | },
hookshot | _ended: true,
hookshot | _ending: true,
hookshot | _redirectCount: 0,
hookshot | _redirects: [],
hookshot | _requestBodyLength: 0,
hookshot | _requestBodyBuffers: [],
hookshot | _eventsCount: 3,
hookshot | _onNativeResponse: [Function (anonymous)],
hookshot | _currentRequest: ClientRequest {
hookshot | _events: [Object: null prototype],
hookshot | _eventsCount: 7,
hookshot | _maxListeners: undefined,
hookshot | outputData: [],
hookshot | outputSize: 0,
hookshot | writable: true,
hookshot | destroyed: false,
hookshot | _last: true,
hookshot | chunkedEncoding: false,
hookshot | shouldKeepAlive: true,
hookshot | maxRequestsOnConnectionReached: false,
hookshot | _defaultKeepAlive: true,
hookshot | useChunkedEncodingByDefault: false,
hookshot | sendDate: false,
hookshot | _removedConnection: false,
hookshot | _removedContLen: false,
hookshot | _removedTE: false,
hookshot | strictContentLength: false,
hookshot | _contentLength: 0,
hookshot | _hasBody: true,
hookshot | _trailer: '',
hookshot | finished: true,
hookshot | _headerSent: true,
hookshot | _closed: false,
hookshot | socket: [TLSSocket],
hookshot | _header: 'GET /_matrix/federation/v1/openid/userinfo?access_token=SOME_TOKEN HTTP/1.1\r\n' +
hookshot | 'Accept: application/json, text/plain, */*\r\n' +
hookshot | 'Host: MY_DOMAIN\r\n' +
hookshot | 'User-Agent: axios/0.27.2\r\n' +
hookshot | 'Connection: keep-alive\r\n' +
hookshot | '\r\n',
hookshot | _keepAliveTimeout: 0,
hookshot | _onPendingData: [Function: nop],
hookshot | agent: [Agent],
hookshot | socketPath: undefined,
hookshot | method: 'GET',
hookshot | maxHeaderSize: undefined,
hookshot | insecureHTTPParser: undefined,
hookshot | joinDuplicateHeaders: undefined,
hookshot | path: '/_matrix/federation/v1/openid/userinfo?access_token=SOME_TOKEN',
hookshot | _ended: false,
hookshot | res: null,
hookshot | aborted: false,
hookshot | timeoutCb: [Function: emitRequestTimeout],
hookshot | upgradeOrConnect: false,
hookshot | parser: null,
hookshot | maxHeadersCount: null,
hookshot | reusedSocket: false,
hookshot | host: 'MY_DOMAIN',
hookshot | protocol: 'https:',
hookshot | _redirectable: [Circular *1],
hookshot | [Symbol(shapeMode)]: false,
hookshot | [Symbol(kCapture)]: false,
hookshot | [Symbol(kBytesWritten)]: 0,
hookshot | [Symbol(kNeedDrain)]: false,
hookshot | [Symbol(corked)]: 0,
hookshot | [Symbol(kOutHeaders)]: [Object: null prototype],
hookshot | [Symbol(errored)]: null,
hookshot | [Symbol(kHighWaterMark)]: 16384,
hookshot | [Symbol(kRejectNonStandardBodyWrites)]: false,
hookshot | [Symbol(kUniqueHeaders)]: null
hookshot | },
hookshot | _currentUrl: 'https://MY_DOMAIN:8448/_matrix/federation/v1/openid/userinfo?access_token=SOME_TOKEN',
hookshot | [Symbol(shapeMode)]: true,
hookshot | [Symbol(kCapture)]: false
hookshot | }
hookshot | }
hookshot | ERROR 16:29:37:923 [ProvisioningApi] ApiError: API error M_AS_BAD_OPENID: Failed to exchange token
hookshot | at BridgeWidgetApi.postExchangeOpenId (/usr/bin/matrix-hookshot/node_modules/matrix-appservice-bridge/lib/provisioning/api.js:321:19)
hookshot | at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
hookshot | error: 'Failed to exchange token',
hookshot | errcode: 'M_AS_BAD_OPENID',
hookshot | statusCode: 500,
hookshot | additionalContent: {}
hookshot | }
```
to configure `widgets`, I have this in `config.yml` for `hookehost`,
```yaml
listeners:
- port: 9000
bindAddress: 0.0.0.0
resources:
- webhooks
# - port: 9001
# bindAddress: 127.0.0.1
# resources:
# - metrics
# - provisioning
- port: 9002
bindAddress: 0.0.0.0
resources:
- widgets
```
```yaml
widgets:
# (Optional) EXPERIMENTAL support for complimentary widgets
addToAdminRooms: false
# disallowedIpRanges:
# - 127.0.0.0/8
# - 10.0.0.0/8
# - 172.16.0.0/12
# - 192.168.0.0/16
# - 100.64.0.0/10
# - 192.0.0.0/24
# - 169.254.0.0/16
# - 192.88.99.0/24
# - 198.18.0.0/15
# - 192.0.2.0/24
# - 198.51.100.0/24
# - 203.0.113.0/24
# - 224.0.0.0/4
# - ::1/128
# - fe80::/10
# - fc00::/7
# - 2001:db8::/32
# - ff00::/8
# - fec0::/10
roomSetupWidget:
addOnInvite: false
publicUrl: https://MY_DOMAIN/widgetapi/v1/static/
branding:
widgetTitle: Hookshot Configuration
```
and here is my `docker-compose.yml`
```yaml
services:
synapse:
container_name: synapse
image: docker.io/matrixdotorg/synapse:latest
restart: unless-stopped
environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
- SYNAPSE_REPORT_STATS=no
- UID=1000
- GID=1000
volumes:
- /mnt/data/:/data
depends_on:
- synapse-db
networks:
synapse:
ports:
# - 8448:8448/tcp
- 8008:8008/tcp
- 3478:3478/tcp
- 3478:3478/udp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8008"]
interval: 30s
timeout: 5s
retries: 5
synapse-db:
image: docker.io/postgres:15-alpine
container_name: synapse-db
restart: unless-stopped
environment:
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD=PSQL_PASS
- POSTGRES_DB=synapse
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- ./appdata/synapse/db/:/var/lib/postgresql/data
ports:
- 5432:5432
networks:
synapse:
hookshot:
container_name: hookshot
ports:
- 9993:9993
- 9000:9000
- 9002:9002
volumes:
- /mnt/data/hookshot/:/data
image: halfshot/matrix-hookshot:latest
depends_on:
- synapse
- synapse-db
networks:
synapse:
networks:
synapse:
```
I have disabled federation completely as this for private use only but from the `docker` logs it seems like it tried to call some route on `8448` which is the port for `federation`.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the issue's config.yml and docker-compose.yml, then trace the ProvisioningApi token-exchange failure and the request to port 8448. Check how widget provisioning reaches Synapse and compare that path with the disabled federation setup. Done means the Hookshot Configuration widget loads without the 504 or M_AS_BAD_OPENID error, with a reproducible configuration documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, typescript
- Domain
- backend, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100