Anyone using forte? I was trying to federate with a forte instance and got 500 errors, they were coming from symfony.
[2026-02-08T21:49:59.620672+00:00] request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor"" at RouterListener.php line 127 {"exception":"[object] (Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException(code: 0): No route found for \"GET https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor\" at /www/quenop/forte/vendor/symfony/http-kernel/EventListener/RouterListener.php:127)\n
[previous exception] [object] (Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException(code: 0): No routes found for \"/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/\". at /www/quenop/forte/vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php:70)"} []
[2026-02-08T21:49:59.624227+00:00] request.CRITICAL: Exception thrown when handling an exception (Symfony\Component\Routing\Exception\RouteNotFoundException: Unable to generate a URL for the named route "api_jsonld_context" as such route does not exist. at CompiledUrlGenerator.php line 50) {"exception":"[object] (Symfony\\Component\\Routing\\Exception\\RouteNotFoundException(code: 0): Unable to generate a URL for the named route \"api_jsonld_context\" as such route does not exist. at /www/quenop/forte/vendor/symfony/routing/Generator/CompiledUrlGenerator.php:50)"} []
[2026-02-08T21:49:59.624828+00:00] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Routing\Exception\RouteNotFoundException: "Unable to generate a URL for the named route "api_jsonld_context" as such route does not exist." at CompiledUrlGenerator.php line 50 {"exception":"[object] (Symfony\\Component\\Routing\\Exception\\RouteNotFoundException(code: 0): Unable to generate a URL for the named route \"api_jsonld_context\" as such route does not exist. at /www/quenop/forte/vendor/symfony/routing/Generator/CompiledUrlGenerator.php:50)\n[previous exception] [object] (Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException(code: 0): No route found for \"GET https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor\" at /www/quenop/forte/vendor/symfony/http-kernel/EventListener/RouterListener.php:127)\n[previous exception] [object] (Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException(code: 0): No routes found for \"/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/\". at /www/quenop/forte/vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php:70)"} []
[2026-02-08T21:49:59.625992+00:00] request.CRITICAL: Exception thrown when handling an exception (Symfony\Component\Routing\Exception\RouteNotFoundException: Unable to generate a URL for the named route "api_jsonld_context" as such route does not exist. at CompiledUrlGenerator.php line 50) {"exception":"[object] (Symfony\\Component\\Routing\\Exception\\RouteNotFoundException(code: 0): Unable to generate a URL for the named route \"api_jsonld_context\" as such route does not exist. at /www/quenop/forte/vendor/symfony/routing/Generator/CompiledUrlGenerator.php:50)"} []
forte uses Cavage and RFC 9421 HTTP Signatures, both keys stored in channel table. it's using the ed25519 pubkey for the DID but it's signing with rsa key - probably because people aren't using ed25519 for signing yet?
I'm not sure what symfony is used for in forte? anyway i added a simple route to return the pubkey so i could verify the federated connection, but i don't think it's supposed to be handled in symfony. (there are some other symfony things in the log too that might need worked on)
so i added two simple routes
config/routes/apgateway.yaml
apgateway_actor:
path: /.well-known/apgateway/{did}/actor
controller: App\Controller\ApGatewayController::actor
methods: [GET]
requirements:
did: ".+"
config/routes/api_jsonld_context.yaml
api_jsonld_context:
path: /contexts/activitystreams
controller: App\Controller\JsonLdContextController::context
methods: [GET]
and then a simple controller to grab the pubkey out of channels table.
src/Controller/ApGatewayController.php
namespace App\Controller;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
final class ApGatewayController
{
private Connection $db;
public function __construct(Connection $db)
{
$this->db = $db;
}
public function actor(Request $request): JsonResponse
{
$uri = $request->getRequestUri();
if (!preg_match('~did:key:([^/]+)~', $uri, $m)) {
throw new NotFoundHttpException('Invalid DID');
}
$epubkey = $m[1];
$row = $this->db->fetchAssociative(
'SELECT channel_pubkey FROM channel WHERE channel_epubkey = :epubkey LIMIT 1',
['epubkey' => $epubkey]
);
if (!$row || empty($row['channel_pubkey'])) {
throw new NotFoundHttpException('Key not found');
}
$publicKeyPem = trim($row['channel_pubkey']);
return new JsonResponse([
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'id' => $request->getSchemeAndHttpHost() . $uri,
'type' => 'Service',
'preferredUsername' => 'gateway',
'inbox' => null,
'publicKey' => [
'id' => $request->getSchemeAndHttpHost() . $uri . '#rsakey',
'owner' => $request->getSchemeAndHttpHost() . $uri,
'publicKeyPem' => $publicKeyPem
]
], 200, [
'Content-Type' => 'application/activity+json'
]);
}
}
src/Controller/JsonLdContextController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
final class JsonLdContextController
{
public function context(): JsonResponse
{
return new JsonResponse([
'@context' => [
'id' => '@id',
'type' => '@type'
]
]);
}
}
so now instead of 500 error i get the pubkey
# curl -i -H 'Accept: application/activity+json' 'https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor'
HTTP/2 200
server: nginx/1.22.1
content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
access-control-allow-origin: *
access-control-allow-credentials: true
access-control-max-age: 600
set-cookie: _jij=cf6eb9e231bf9f792e89339767137013; path=/; domain=quenop.com; secure; HttpOnly; SameSite=None
link: <did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor>; rel="alternate"
cache-control: public
date: Mon, 09 Feb 2026 03:04:06 GMT
content-digest: sha-256=:8k2GEEBYckStBqiplxgAWvGYtU0oBAl/dJHUvlefDfA=:
signature-input: sig1=("date" "content-digest" "content-type" "@status");keyid="https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor#rsakey";alg="rsa-v1_5-sha256";created=1770606246;expires=1770609846
signature: sig1=:tf+XtDS+IwOv0OWt31D3ATtHloKBW1kjqsCuFfG8+6rjPLBoGqL+FiVoYyVB+n/SvYm7d4ctsc9bxCPzXo8q0TImzAD/gLssmSoQU9BG518ceG5oVodxFnXal17LpcFYdIunQEZ6QX6By906uKEI2ymjdP58bOl2j3FEPEyapVAF4Bf9nFIdUvuMsDjl61uSlRMnKzpp2DIBxQwOv0H3u/sEiX1h1a810HFTj1YMQlnjv27/H36amMF6WRy7By/7EQRUW0R5XDqqPnHrLPlMWuCVtfTDtpp4MOE1MmHK1pXcq+PanrikFUTRo8MzslSLvmHuf8l/gQvHoJaJ0O93Q5WfuNGeWeqW6ksyWYeqbImCkekkgKoMtzW7bTMyhelLtC1pf05tL79xLLncsJKwqI3xr4FYeoeZhqSI7fhmHM3K3Vz+Gvw0E7cnzZFoMw3S046NGf66t0I9rAmlr4jwQK1jfiHT6Cz80zLPIYFR1l08yGznApHtZzVONkKl46IvRBR60ebFAF1P5vfaZefzc5QP6kl9ugqVVTbC2aHrR/igvFgoyS9UQhP9yN93Mlh0rh8B7hqGSxRt7Pt7JuH0V7ofVUipsjl7mrxBE4lIlZfRI1N2D9s2lzi4q/F8S06GwTCiLNuoZVovK3mJT4+5VjzJXrd6I3opWqJoF2gQfZQ=:
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/data-integrity/v2","https://w3id.org/fep/c390","https://w3id.org/fep/844e","https://w3id.org/fep/0499","https://w3id.org/fep/8a8e",{"contextHistory":{"@id":"https://w3id.org/fep/171b/contextHistory","@type":"@id","@container":"@list"},"gateways":{"@id":"https://w3id.org/fep/ef61/gateways","@type":"@id","@container":"@list"},"wf":"https://purl.archive.org/socialweb/webfinger#","xsd":"http://www.w3.org/2001/XMLSchema#","timezone":"http://www.w3.org/2006/time#timezone","webfinger":{"@id":"wf:webfinger","@type":"xsd:string"},"as":"https://www.w3.org/ns/activitystreams#","nomad":"https://purl.org/nomad/apschema#","toot":"http://joinmastodon.org/ns#","manuallyApprovesFollowers":"as:manuallyApprovesFollowers","oauthRegistrationEndpoint":"nomad:oauthRegistrationEndpoint","sensitive":"as:sensitive","alsoKnownAs":"as:alsoKnownAs","movedTo":"https://w3id.org/fep/7628#movedTo","discoverable":"toot:discoverable","indexable":"toot:indexable","Hashtag":"as:Hashtag","canReply":"toot:canReply","canSearch":"nomad:canSearch","expires":"nomad:expires","directMessage":"nomad:directMessage","Category":"nomad:Category","Sync":"nomad:Sync","copiedTo":"https://w3id.org/fep/7628#copiedTo","permissions":"nomad:permissions","searchContent":"nomad:searchContent","searchTags":"nomad:searchTags","collectionOf":"nomad:collectionOf","openWebAuth":"nomad:openWebAuth","owaRedirect":"nomad:owaRedirect"}],"id":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor","type":"Person","attachment":[{"type":"VerifiableIdentityStatement","subject":"did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8","alsoKnownAs":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor","proof":{"type":"DataIntegrityProof","cryptosuite":"eddsa-jcs-2022","created":"2026-02-09T03:04:06Z","verificationMethod":"did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8","proofPurpose":"assertionMethod","proofValue":"z4UwyoruSVfVUvkZM6qfTBLzW7wJunm163B8NErNw2bNQUdAXSDL2LcPM3oRdwaGdVKnE8xdtYGpD5LF2DWjgFnW8"}},{"type":"Note","name":"region","content":"California"},{"type":"Note","name":"country_name","content":"United States"},{"type":"Note","name":"hometown","content":"Los Altos"},{"type":"Note","name":"gender","content":"Male"},{"type":"Note","name":"marital","content":"It's complicated"},{"type":"Note","name":"sexual","content":"Females"},{"type":"Note","name":"homepage","content":"https://quantificant.com"},{"type":"Note","name":"contact","content":"https://quantificant.com/contact"},{"type":"Note","name":"birthday","content":"0000-00-00"}],"name":"status","generator":{"type":"Application","name":"quenop","implements":[{"name":"RFC-9421: HTTP Message Signatures","href":"https://datatracker.ietf.org/doc/html/rfc9421"}]},"icon":{"type":"Image","mediaType":"image/png","updated":"2025-11-05T02:16:45Z","url":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/photo/profile/l/2","height":300,"width":300},"published":"2025-11-05T01:32:54Z","tag":[{"type":"Hashtag","id":"https://quenop.com/search?tag=fediverse","name":"#fediverse"},{"type":"Hashtag","id":"https://quenop.com/search?tag=forte","name":"#forte"},{"type":"Hashtag","id":"https://quenop.com/search?tag=activitypub","name":"#activitypub"}],"updated":"2025-11-05T01:32:54Z","url":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor","canSearch":[],"inbox":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/inbox","outbox":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/outbox","followers":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/followers","following":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/following","endpoints":{"sharedInbox":"https://quenop.com/inbox","multibox":"https://quenop.com/inbox/multibox","proxyUrl":"https://quenop.com/api/proxy","oauthRegistrationEndpoint":"https://quenop.com/api/client/register","oauthAuthorizationEndpoint":"https://quenop.com/authorize","oauthTokenEndpoint":"https://quenop.com/token","searchContent":"https://quenop.com/search/status?search={}","searchTags":"https://quenop.com/search/status?tag={}","openWebAuth":"https://quenop.com/owa","owaRedirect":"https://quenop.com/magic"},"publicKey":{"id":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor#rsakey","owner":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor","signatureAlgorithm":"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwnZ2I+8TRfzBVjbw3AUn\nHO6luSS0kzsw3jX6y9MBcxw81ujCYLLRqXGHH015Pfg5kDVsca/6OU8i1L24D1PG\nTYKzpL7/js4DB5SPQYlhQdgdVwel0ZxmFiXT9LPiH9fiIgfkAV1NUGPrH9VTP8Wq\nI/uLtdSr6PVQTKtFgnv4EgasqZp4/NdLKYuNaUTDPgNtpUqZFLLZF+ARmMyX/hFe\nfX/OllZc2uppfuVrKlr+5bNvLb44HNxSQ6epDjETloEMjyuo3CmuCAmJUy5C8pIy\nydLoHCYnAczfdJ9W3vkjvDQ01pRdTGlnSPxlT0HGZ+/oCvXwCmN5FzAAETZX3jxq\nuBi8z1a18+LFrsSHEA0WI8tgc2GAmU6CYkufdufSURUYL/oVK67+gNtdqFHpIb01\n9qN8KyrbZuzebZMfN49XZJWFG3NiGoCMBHDJA8zpLk6JJT5exFEyKryI5xmDEcPu\nXDZt98kAXBxivetUQKOFcqYY2XYncz5dgTGikwPMEieT8oDXoJWTuNJITJyrB0Mo\nxOSF0EHR8vHYTmpCW+1romlDKNs6/v3isczJad5jV9hFypCq7UhwdhTexqco9IJ4\ndG08LVuitpZrEBB8t7DQnzHiE0m9Adu2Rpv22FKQG9ddKN0QoTaDCxCjPT70hK4t\nuEpKnil61Rw3xbv0hNNK8XMCAwEAAQ==\n-----END PUBLIC KEY-----\n"},"preferredUsername":"status","alsoKnownAs":["https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor","https://quenop.com/channel/status"],"streams":[{"name":"Photo Albums","id":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/albums"}],"permissions":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor/permissions","discoverable":true,"manuallyApprovesFollowers":true,"webfinger":"status@quenop.com","indexable":false,"assertionMethod":[{"id":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor#ed25519key","type":"Multikey","controller":"https://quenop.com/.well-known/apgateway/did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8/actor","publicKeyMultibase":"z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8"}],"gateways":["https://quenop.com"],"proof":{"type":"DataIntegrityProof","cryptosuite":"eddsa-jcs-2022","created":"2026-02-09T03:04:06Z","verificationMethod":"did:key:z6MkfSopYjZdbYEjRHWK3XqBZoNgyRJAFKG6w2uovZsRh8R8","proofPurpose":"assertionMethod","proofValue":"z3PYcYfeYuFpAm2fBc2N45dmhfnLeEn3DkpydmGb5E5J2bZYzsPrWDHuSggdzQ8fTDz9XL8EivrFDw2rpbw8W329S"}}
so - what am i missing? there;s a FEDERATION.md that says it federates. it didn't appear that symfony is configured - no db info (was trying to connect to postgresql when i'm using mysql - but that's a default symfony thing)
but one thing is: it was causing 500 errors, i added the code stuff, and then it returned the json as scripted. HOWEVER now it is returning a much larger json array, not created by symfony any more obviously :) not sure why it decided to change it's mind and work?
i have a hunch the very first request when you do a connection request is off - get hitting synfony instead Mike's software - but then something changes and were bypassing the symfony as myabe it should have done in the first place..
any ideas?
i could submit a issue or pr on git but i just have a big mess to sort through first lol.