Serve the OpenAccess descriptor at /.well-known/openaccess.json (#127)
Some checks failed
CI / build (push) Has been cancelled
deploy / deploy (push) Has been cancelled
test / test (push) Has been cancelled

Spec: https://logicsrc.com/openaccess

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01SWRffW4ifQPUrGXJtgYWMd
This commit is contained in:
Anthony Ettinger 2026-09-12 12:26:25 -07:00 • committed by GitHub
parent d2e4b56ae7
commit aad61eec17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 101 additions and 0 deletions

View file

@ -0,0 +1,29 @@
{
"openaccess": "0.1",
"name": "AgentBBS",
"url": "https://bbs.profullstack.com",
"operator": "https://logicsrc.com/.well-known/openprofile.md",
"redirect_uris": [
"https://bbs.profullstack.com/api/v1/openaccess/callback"
],
"jwks": {
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"kid": "Vg7JJd0byYl3",
"x": "bck0ITevVM5tl-uMt-IAzuD_4DBuTpn9zlE0WKimdxw",
"alg": "EdDSA",
"use": "sig"
}
]
},
"scopes": {},
"honours": [
"profullstack.com/all-access"
],
"webhooks": "https://bbs.profullstack.com/api/v1/openaccess/events",
"hubs": [
"https://openaccess.logicsrc.com"
]
}

View file

@ -2,6 +2,7 @@ package files
import (
"crypto/rand"
_ "embed"
"encoding/hex"
"errors"
"fmt"
@ -71,9 +72,40 @@ func (s *Service) WebHandler(cfg WebConfig) http.Handler {
mux.HandleFunc("/mkdir", h.handleMkdir)
mux.HandleFunc("/delete", h.handleDelete)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("ok")) })
mux.Handle(OpenAccessPath, OpenAccessHandler())
return mux
}
// OpenAccessPath is the well-known location of the OpenAccess descriptor
// (https://logicsrc.com/openaccess). Hubs such as openaccess.logicsrc.com fetch
// it to list the BBS and to link accounts with OAuth 2.1 + PKCE.
const OpenAccessPath = "/.well-known/openaccess.json"
// openAccessDescriptor is the static descriptor served verbatim. It names the
// public signing key (JWKS), the redirect URI and the hubs the BBS trusts.
//
//go:embed openaccess.json
var openAccessDescriptor []byte
// OpenAccessHandler serves the embedded OpenAccess descriptor as JSON with a
// short public cache. It needs no session: hubs fetch it anonymously.
func OpenAccessHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
w.Header().Set("Allow", "GET, HEAD")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "public, max-age=300")
w.Header().Set("Content-Length", strconv.Itoa(len(openAccessDescriptor)))
if r.Method == http.MethodHead {
return
}
_, _ = w.Write(openAccessDescriptor)
})
}
// --- session helpers --------------------------------------------------------
func (h *webSrv) lookup(r *http.Request) (string, bool) {

View file

@ -2,6 +2,7 @@ package files
import (
"bytes"
"encoding/json"
"io"
"mime"
"mime/multipart"
@ -394,3 +395,33 @@ func TestWebPublicReadOnlyByDefault(t *testing.T) {
t.Fatalf("public upload: want redirect, got %d", rr.Code)
}
}
func TestWebOpenAccessDescriptor(t *testing.T) {
h, _ := webTestHandler(t)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, OpenAccessPath, nil))
if rr.Code != http.StatusOK {
t.Fatalf("status: want 200, got %d", rr.Code)
}
if ct := rr.Header().Get("Content-Type"); ct != "application/json" {
t.Fatalf("content-type: got %q", ct)
}
if cc := rr.Header().Get("Cache-Control"); cc != "public, max-age=300" {
t.Fatalf("cache-control: got %q", cc)
}
var doc struct {
OpenAccess string `json:"openaccess"`
URL string `json:"url"`
}
if err := json.Unmarshal(rr.Body.Bytes(), &doc); err != nil {
t.Fatalf("descriptor is not JSON: %v", err)
}
if doc.OpenAccess == "" || doc.URL == "" {
t.Fatalf("descriptor missing openaccess/url: %s", rr.Body.String())
}
rr = httptest.NewRecorder()
h.ServeHTTP(rr, httptest.NewRequest(http.MethodPost, OpenAccessPath, nil))
if rr.Code != http.StatusMethodNotAllowed {
t.Fatalf("POST: want 405, got %d", rr.Code)
}
}