Add /llms.txt endpoint to trust center compliance page

Renders compliance data (frameworks, documents, audits, subprocessors, references, and external links) as a plain-text Markdown document. The endpoint supports all required visibility filters and pagination.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-19 22:05:52 +01:00
parent 19eb2f5d84
commit f11eae6b82
4 changed files with 503 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package compliancepage
import (
"net/http"
"go.probo.inc/probo/pkg/trust"
)
type Handler struct {
trustService *trust.Service
}
func NewHandler(trustService *trust.Service) *Handler {
return &Handler{trustService: trustService}
}
func (h *Handler) HandleLLMsTxt(w http.ResponseWriter, r *http.Request) {
tc := CompliancePageFromContext(r.Context())
if tc == nil {
http.Error(w, "not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
if err := h.trustService.RenderCompliancePageMarkdown(r.Context(), w, tc.ID, tc.TenantID); err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
}
}

View File

@@ -174,7 +174,10 @@ func (s *Server) stripTrustPrefix(next http.Handler) http.Handler {
func (s *Server) trustCenterRouter() chi.Router {
r := chi.NewRouter()
h := compliancepage.NewHandler(s.trustService)
r.Mount("/api/trust/v1", s.apiServer.CompliancePageHandler())
r.Get("/llms.txt", h.HandleLLMsTxt)
r.Handle("/*", s.trustWebServer)
return r

View File

@@ -0,0 +1,40 @@
# {{ .OrgName }} — Compliance
{{ if .Description }}
{{ .Description }}
{{ end }}{{ if .Details }}
{{ range .Details }}- **{{ .Label }}**: {{ .Value }}
{{ end }}{{ end }}{{ if .Frameworks }}
## Compliance Frameworks
{{ range .Frameworks }}- {{ .Name }}{{ if .Description }}: {{ .Description }}{{ end }}
{{ end }}{{ end }}{{ if .Documents }}
## Documents
| Title | Type |
|-------|------|
{{ range .Documents }}| {{ .Title }} | {{ .Type }} |
{{ end }}{{ end }}{{ if .Audits }}
## Audits
| Name | Framework | Valid From | Valid Until |
|------|-----------|------------|-------------|
{{ range .Audits }}| {{ .Name }} | {{ .Framework }} | {{ .ValidFrom }} | {{ .ValidUntil }} |
{{ end }}{{ end }}{{ if .Vendors }}
## Subprocessors
| Name | Category | Countries | Website |
|------|----------|-----------|---------|
{{ range .Vendors }}| {{ .Name }} | {{ .Category }} | {{ .Countries }} | {{ .Website }} |
{{ end }}{{ end }}{{ if .References }}
## References
| Name | Description | Website |
|------|-------------|---------|
{{ range .References }}| {{ .Name }} | {{ .Description }} | {{ .Website }} |
{{ end }}{{ end }}{{ if .ExternalLinks }}
## External Links
| Name | URL |
|------|-----|
{{ range .ExternalLinks }}| {{ .Name }} | {{ .URL }} |
{{ end }}{{ end }}

View File

@@ -0,0 +1,417 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package trust
import (
"context"
_ "embed"
"fmt"
"io"
"strings"
"text/template"
"go.gearno.de/x/ref"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
//go:embed compliance.md.tmpl
var complianceTmplContent string
var complianceTmpl = template.Must(template.New("compliance").Parse(complianceTmplContent))
type (
compliancePageData struct {
OrgName string
Description string
Details []compliancePageDetail
Frameworks []compliancePageFramework
Documents []compliancePageDocument
Audits []compliancePageAudit
Vendors []compliancePageVendor
References []compliancePageReference
ExternalLinks []compliancePageExternalLink
}
compliancePageDetail struct {
Label string
Value string
}
compliancePageFramework struct {
Name string
Description string
}
compliancePageDocument struct {
Title string
Type string
}
compliancePageAudit struct {
Name string
Framework string
ValidFrom string
ValidUntil string
}
compliancePageVendor struct {
Name string
Category string
Countries string
Website string
}
compliancePageReference struct {
Name string
Description string
Website string
}
compliancePageExternalLink struct {
Name string
URL string
}
)
func (s *Service) RenderCompliancePageMarkdown(
ctx context.Context,
w io.Writer,
trustCenterID gid.GID,
tenantID gid.TenantID,
) error {
org, err := s.GetOrganizationByTrustCenterID(ctx, trustCenterID)
if err != nil {
return fmt.Errorf("cannot load organization for compliance page: %w", err)
}
tenantSvc := s.WithTenant(tenantID)
data := &compliancePageData{
OrgName: org.Name,
}
if org.Description != nil && *org.Description != "" {
data.Description = *org.Description
}
if org.WebsiteURL != nil && *org.WebsiteURL != "" {
data.Details = append(data.Details, compliancePageDetail{Label: "Website", Value: *org.WebsiteURL})
}
if org.Email != nil && *org.Email != "" {
data.Details = append(data.Details, compliancePageDetail{Label: "Email", Value: *org.Email})
}
if org.HeadquarterAddress != nil && *org.HeadquarterAddress != "" {
data.Details = append(data.Details, compliancePageDetail{Label: "Headquarters", Value: *org.HeadquarterAddress})
}
data.Frameworks = s.fetchComplianceFrameworks(ctx, tenantSvc, trustCenterID)
data.Documents = s.fetchDocuments(ctx, tenantSvc, org.ID)
data.Audits = s.fetchAudits(ctx, tenantSvc, org.ID)
data.Vendors = s.fetchVendors(ctx, tenantSvc, org.ID)
data.References = s.fetchReferences(ctx, tenantSvc, trustCenterID)
data.ExternalLinks = s.fetchExternalLinks(ctx, tenantSvc, trustCenterID)
if err := complianceTmpl.Execute(w, data); err != nil {
return fmt.Errorf("cannot render compliance page markdown: %w", err)
}
return nil
}
func (s *Service) fetchComplianceFrameworks(ctx context.Context, tenantSvc *TenantService, trustCenterID gid.GID) []compliancePageFramework {
var frameworks []compliancePageFramework
var cursorKey *page.CursorKey
for {
cursor := page.NewCursor(
page.MaxCursorSize,
cursorKey,
page.Head,
page.OrderBy[coredata.ComplianceFrameworkOrderField]{
Field: coredata.ComplianceFrameworkOrderFieldRank,
Direction: page.OrderDirectionAsc,
},
)
result, err := tenantSvc.ComplianceFrameworks.ListByTrustCenterID(ctx, trustCenterID, cursor)
if err != nil {
break
}
for _, cf := range result.Data {
if cf.Visibility != coredata.ComplianceFrameworkVisibilityPublic {
continue
}
fw, err := tenantSvc.Frameworks.Get(ctx, cf.FrameworkID)
if err != nil {
continue
}
fi := compliancePageFramework{Name: fw.Name}
if fw.Description != nil {
fi.Description = *fw.Description
}
frameworks = append(frameworks, fi)
}
if !result.Info.HasNext {
break
}
last := result.Data[len(result.Data)-1]
ck := last.CursorKey(coredata.ComplianceFrameworkOrderFieldRank)
cursorKey = &ck
}
return frameworks
}
func (s *Service) fetchDocuments(ctx context.Context, tenantSvc *TenantService, orgID gid.GID) []compliancePageDocument {
var docs []compliancePageDocument
var cursorKey *page.CursorKey
for {
cursor := page.NewCursor(
page.MaxCursorSize,
cursorKey,
page.Head,
page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldTitle,
Direction: page.OrderDirectionAsc,
},
)
result, err := tenantSvc.Documents.ListForOrganizationId(ctx, orgID, cursor)
if err != nil {
break
}
for _, doc := range result.Data {
if doc.TrustCenterVisibility != coredata.TrustCenterVisibilityPublic {
continue
}
docs = append(
docs,
compliancePageDocument{
Title: doc.Title,
Type: doc.DocumentType.String(),
},
)
}
if !result.Info.HasNext {
break
}
last := result.Data[len(result.Data)-1]
ck := last.CursorKey(coredata.DocumentOrderFieldTitle)
cursorKey = &ck
}
return docs
}
func (s *Service) fetchAudits(ctx context.Context, tenantSvc *TenantService, orgID gid.GID) []compliancePageAudit {
var audits []compliancePageAudit
var cursorKey *page.CursorKey
for {
cursor := page.NewCursor(
page.MaxCursorSize,
cursorKey,
page.Head,
page.OrderBy[coredata.AuditOrderField]{
Field: coredata.AuditOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
},
)
result, err := tenantSvc.Audits.ListForOrganizationId(ctx, orgID, cursor)
if err != nil {
break
}
for _, audit := range result.Data {
if audit.TrustCenterVisibility != coredata.TrustCenterVisibilityPublic {
continue
}
frameworkName := ""
fw, err := tenantSvc.Frameworks.Get(ctx, audit.FrameworkID)
if err == nil {
frameworkName = fw.Name
}
ai := compliancePageAudit{
Name: ref.UnrefOrZero(audit.Name),
Framework: frameworkName,
}
if audit.ValidFrom != nil {
ai.ValidFrom = audit.ValidFrom.Format("2006-01-02")
}
if audit.ValidUntil != nil {
ai.ValidUntil = audit.ValidUntil.Format("2006-01-02")
}
audits = append(audits, ai)
}
if !result.Info.HasNext {
break
}
last := result.Data[len(result.Data)-1]
ck := last.CursorKey(coredata.AuditOrderFieldCreatedAt)
cursorKey = &ck
}
return audits
}
func (s *Service) fetchVendors(ctx context.Context, tenantSvc *TenantService, orgID gid.GID) []compliancePageVendor {
var vendors []compliancePageVendor
var cursorKey *page.CursorKey
for {
cursor := page.NewCursor(
page.MaxCursorSize,
cursorKey,
page.Head,
page.OrderBy[coredata.VendorOrderField]{
Field: coredata.VendorOrderFieldName,
Direction: page.OrderDirectionAsc,
},
)
result, err := tenantSvc.Vendors.ListForOrganizationId(ctx, orgID, cursor)
if err != nil {
break
}
for _, v := range result.Data {
var countries []string
for _, c := range v.Countries {
countries = append(countries, c.String())
}
vendors = append(
vendors,
compliancePageVendor{
Name: v.Name,
Category: v.Category.String(),
Countries: strings.Join(countries, ", "),
Website: ref.UnrefOrZero(v.WebsiteURL),
},
)
}
if !result.Info.HasNext {
break
}
last := result.Data[len(result.Data)-1]
ck := last.CursorKey(coredata.VendorOrderFieldName)
cursorKey = &ck
}
return vendors
}
func (s *Service) fetchReferences(ctx context.Context, tenantSvc *TenantService, trustCenterID gid.GID) []compliancePageReference {
var refs []compliancePageReference
var cursorKey *page.CursorKey
for {
cursor := page.NewCursor(
page.MaxCursorSize,
cursorKey,
page.Head,
page.OrderBy[coredata.TrustCenterReferenceOrderField]{
Field: coredata.TrustCenterReferenceOrderFieldRank,
Direction: page.OrderDirectionAsc,
},
)
result, err := tenantSvc.TrustCenterReferences.ListForTrustCenterID(ctx, trustCenterID, cursor)
if err != nil {
break
}
for _, r := range result.Data {
ri := compliancePageReference{
Name: r.Name,
Website: r.WebsiteURL,
}
if r.Description != nil {
ri.Description = *r.Description
}
refs = append(refs, ri)
}
if !result.Info.HasNext {
break
}
last := result.Data[len(result.Data)-1]
ck := last.CursorKey(coredata.TrustCenterReferenceOrderFieldRank)
cursorKey = &ck
}
return refs
}
func (s *Service) fetchExternalLinks(ctx context.Context, tenantSvc *TenantService, trustCenterID gid.GID) []compliancePageExternalLink {
var links []compliancePageExternalLink
var cursorKey *page.CursorKey
for {
cursor := page.NewCursor(
page.MaxCursorSize,
cursorKey,
page.Head,
page.OrderBy[coredata.ComplianceExternalURLOrderField]{
Field: coredata.ComplianceExternalURLOrderFieldRank,
Direction: page.OrderDirectionAsc,
},
)
result, err := tenantSvc.ComplianceExternalURLs.ListForTrustCenterID(ctx, trustCenterID, cursor)
if err != nil {
break
}
for _, l := range result.Data {
links = append(
links,
compliancePageExternalLink{
Name: l.Name,
URL: l.URL,
},
)
}
if !result.Info.HasNext {
break
}
last := result.Data[len(result.Data)-1]
ck := last.CursorKey(coredata.ComplianceExternalURLOrderFieldRank)
cursorKey = &ck
}
return links
}