404 error on Activitypub (/.ghost/activitypub/v1/site)

Finally I had installed Ghost 6 with Activitypub local server on Docker without using Caddy and/or Traeffik.

I can see the Network UI and configure my profile.

Everything seems to be fine but I see 404 errors on specific pages (/activitypub/v1/site /activitypub/v1/recommendations /activitypub/v1/topics).

curl -i “``https://mydomain.tld.ghost/activitypub/v1/site``” shows {“error”:“Forbidden”,“code”:“ROLE_MISSING”}

docker activitypub container shows ERR activitypub: User role ‘Anonymous’ is not allowed to access this resource

I wonder what could be wrong? That’s my files:

services:

  ghost:
    image: ghost:${GHOST_VERSION:-6-alpine}
    restart: always
    ports:
      - "2368:2368"
    env_file:
      - .env
    environment:
      NODE_ENV: production
      url: https://${DOMAIN:?DOMAIN environment variable is required}
      admin__url: ${ADMIN_DOMAIN:+https://${ADMIN_DOMAIN}}
      ap__enabled: "true"
      ap__domain: "mydomain.tld"
      ap__actor: "https://mydomain.tld/activitypub/actor"
      ap__storage_adapter: "filesystem"
      ap__storage_filesystem__path: "/var/lib/ghost/activitypub"
      database__client: mysql
      database__connection__host: db
      database__connection__user: ${DATABASE_USER:-ghost}
      database__connection__password: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      database__connection__database: ghost
    volumes:
      - ${UPLOAD_LOCATION:-./data/ghost}:/var/lib/ghost/content
      - ap_data:/var/lib/ghost/activitypub
    depends_on:
      db:
        condition: service_healthy
      activitypub:
        condition: service_started
        required: false
    networks:
      - ghost_network
      
  db:
    image: mysql:8.0.42@sha256:4445b2668d41143cb50e471ee207f8822006249b6859b24f7e12479684def5d9
    restart: always
    expose:
      - "3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD:?DATABASE_ROOT_PASSWORD environment variable is required}
      MYSQL_USER: ${DATABASE_USER:-ghost}
      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      MYSQL_DATABASE: ghost
      MYSQL_MULTIPLE_DATABASES: activitypub
    volumes:
      - ${MYSQL_DATA_LOCATION:-./data/mysql}:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d
    healthcheck:
      test: mysqladmin ping -p$$MYSQL_ROOT_PASSWORD -h 127.0.0.1
      interval: 1s
      start_period: 30s
      start_interval: 10s
      retries: 120
    networks:
      - ghost_network

  activitypub:
    image: ghcr.io/tryghost/activitypub:1.1.0@sha256:39c212fe23603b182d68e67d555c6b9b04b1e57459dfc0bef26d6e4980eb04d1
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - ${UPLOAD_LOCATION:-./data/ghost}:/opt/activitypub/content
    environment:
      # See https://github.com/TryGhost/ActivityPub/blob/main/docs/env-vars.md
      NODE_ENV: production
      ACTIVITYPUB_COLLECTION_PAGE_SIZE: 20
      MYSQL_HOST: db
      MYSQL_USER: ${DATABASE_USER:-ghost}
      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      MYSQL_DATABASE: activitypub
      LOCAL_STORAGE_PATH: /opt/activitypub/content/images/activitypub
      LOCAL_STORAGE_HOSTING_URL: https://${DOMAIN}/content/images/activitypub
    depends_on:
      db:
        condition: service_healthy
#      activitypub-migrate:
#        condition: service_completed_successfully
    profiles: [activitypub]
    networks:
      - ghost_network

#  activitypub-migrate:
#    image: ghcr.io/tryghost/activitypub-migrations:1.1.0@sha256:b3ab20f55d66eb79090130ff91b57fe93f8a4254b446c2c7fa4507535f503662
#    environment:
#      MYSQL_DB: mysql://${DATABASE_USER:-ghost}:${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}@tcp(db:3306)/activitypub
#    networks:
#      - ghost_network
#    depends_on:
#      db:
#        condition: service_healthy
#    profiles: [activitypub]
#    restart: no

volumes:
  ap_data:

networks:
  ghost_network:
server {
    listen 443 ssl http2;
    server_name mydomain.tld;
    ssl_certificate /etc/letsencrypt/live/mydomain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mydomain.tld/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

client_max_body_size 300m;

    location ^~ /.ghost/activitypub/ {
    proxy_pass              http://127.0.0.1:8080;
    proxy_set_header        Host              $host;
    proxy_set_header        Authorization     $http_authorization;
    proxy_set_header        X-Real-IP         $remote_addr;
    proxy_set_header        X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;

        # Websocket support (often needed for real-time status)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

    proxy_no_cache          1;
    proxy_cache_bypass      1;
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    }

    location ~ ^/.well-known/(webfinger|nodeinfo) {
        proxy_set_header Authorization $http_authorization;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
#        proxy_ssl_server_name on;
        proxy_pass http://127.0.0.1:8080;
#        add_header X-Content-Type-Options nosniff;
}

    # Enable CORS for ActivityPub inbox and outbox if desired
    location ~* ^/activitypub/(inbox|outbox|followers) {
        add_header Access-Control-Allow-Origin *;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8080;
    }

location / {

        proxy_pass http://127.0.0.1:2368;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;

        proxy_redirect off;
        proxy_buffering off;
        proxy_read_timeout 300s;

    # WebSocket support for real-time admin (test ghost_backend)
    location /ghost/socket {
        proxy_pass               http://127.0.0.1:2368;
        proxy_http_version       1.1;
        proxy_set_header         Upgrade $http_upgrade;
        proxy_set_header         Connection "upgrade";
        proxy_set_header         Host $host;
    }

}

    proxy_connect_timeout 90s;
    proxy_send_timeout 90s;
    proxy_read_timeout 90s;

add_header Access-Control-Allow-Origin mydomain.tld;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src * data: blob: 'unsafe-inline' 'unsafe-eval'";
add_header Content-Security-Policy "img-src * https: blob: data:";
#add_header Content-Security-Policy "default-src https: data:";
add_header Content-Security-Policy "object-src 'none'";
add_header Content-Security-Policy "script-src MYSCRIPTS 'unsafe-inline'";
add_header Content-Security-Policy "style-src 'unsafe-inline' https:";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN";
proxy_hide_header X-Frame-Options;

}

Where are you seeing these 404s that’s generating user-facing error?

Hi Mark, I see those errors on the browser console, they prevent to explore ActivityPub from the Ghost Admin UI.

The same error is seen on /.ghost/activitypub/stable/client-config URL\ as well.

Did you find a solution to this?

I am seeing the same error. Activitypub seems to be federating in that I can see things when searching for @index@mysite.com on Hachyderm but when I go to the Network section of the Admin area in Ghost I get Oops, page not found! From there, the Explore section seems to work, I can see I followed the site and the logs show a 200 response when I click Explore:

12:30:36.936 INF activitypub: 'GET' 'mysite.com' 'https://mysite.com/.ghost/activitypub/v1/notifications' 'REDACTED'
12:30:36.937 INF activitypub: KnexKvStore: Get key cachedJwks,mysite.com
12:30:36.980 INF activitypub: 'GET' 'mysite.com' 'https://mysite.com/.ghost/activitypub/v1/notifications' 'REDACTED' 200 44ms

When I click on Reader, in the logs I see:

12:34:35.120 INF activitypub: 'GET' 'mysite.com' 'https://mysite.com/.ghost/activitypub/v1/recommendations/?limit=3' 'REDACTED'
12:34:35.120 INF activitypub: KnexKvStore: Get key cachedJwks,separationofconcerns.dev
12:34:35.136 WRN fedify·federation·http: 'GET' '/.ghost/activitypub/v1/recommendations/?limit=3': 404
12:34:35.136 INF activitypub: 'GET' 'mysite.com' 'https://mysite.com/.ghost/activitypub/v1/recommendations/?limit=3' 'REDACTED' 404 16ms
12:34:35.593 INF activitypub: 'GET' 'mysite.com' 'https://mysite.com/.ghost/activitypub/v1/recommendations/?limit=3' 'REDACTED'
12:34:35.594 INF activitypub: KnexKvStore: Get key cachedJwks,mysite.com
12:34:35.602 WRN fedify·federation·http: 'GET' '/.ghost/activitypub/v1/recommendations/?limit=3': 404
12:34:35.602 INF activitypub: 'GET' 'mysite.com' 'https://mysite.com/.ghost/activitypub/v1/recommendations/?limit=3' 'REDACTED' 404 9ms

Hi there, nope. It seems to be a limitation from Ghost (to ap.ghost.org) on those specific routes.

The rest of Activitypub infraestructure just works. So you can lean on that and forget to 'explore‘ from the Ghost UI.

Thanks for the reply. I did a little more digging and it looks like this PR from back in early December might fix some of this. It looks like ActivityPub 1.1.0 shipped without the endpoint(s) and this PR was merged after 1.1.0 was tagged so its waiting on the next release whenever that will be. Hopefully soon as I saw some other PRs that have come in since 1.1.0 that look important.

Yep, I see that too. I’ve updated to 1.1.0 and get the same errors.

Perhaps some member of the team can give us a light on this :)

The tagged versions of the activitypub image are quite old. If you want access to the newest stuff, you can use ghcr.io/tryghost/activitypub:edge as image in your Docker setups.

Though, it is well…bleeding edge :upside_down_face: