Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions EXAMPLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```bash
# Push manifest
oras push localhost:8080/dir:example-agent \
--artifact-type application/vnd.aaif.ai.manifest.v1+json \
--annotation "org.aaif.ai.card.id=did:example:agent-finance-001" \
--annotation "org.aaif.ai.card.specVersion=1.0" \
--annotation "org.opencontainers.image.created=2026-03-10T15:00:00Z" \
README.md:application/vnd.oasf.card.v1+json

# Generate sample signing key
notation cert delete --type ca --store ai-example.io --all -y
notation cert generate-test --default "ai-example.io"

# Sign using notation
notation sign localhost:8080/dir:example-agent

# Create and push AI Catalog with example-agent
oras manifest index create \
localhost:8080/dir:catalog \
--artifact-type "application/vnd.aaif.ai.catalog.v1+json" \
example-agent

```
2 changes: 1 addition & 1 deletion install/docker/apiserver.env
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,5 @@ DIRECTORY_SERVER_RATELIMIT_PER_CLIENT_BURST=200
# Logger Configuration (shared across components)
# Log levels: DEBUG, INFO, WARN, ERROR
# Log formats: json, text
DIRECTORY_LOGGER_LOG_LEVEL=DEBUG
DIRECTORY_LOGGER_LOG_LEVEL=INFO
DIRECTORY_LOGGER_LOG_FORMAT=json
3 changes: 2 additions & 1 deletion install/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ services:
env_file:
- ./apiserver.env
ports:
- 8888:8888
- 8888:8888 # DIR API server
- 8080:8080 # OCI HTTP server
depends_on:
zot:
condition: service_started
Expand Down
1 change: 1 addition & 0 deletions reconciler/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
require (
buf.build/gen/go/agntcy/oasf-sdk/protocolbuffers/go v1.36.11-20260225090442-549b0cc62759.1 // indirect
buf.build/gen/go/agntcy/oasf/protocolbuffers/go v1.36.11-20260311134852-004f24f91cc7.1 // indirect
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
Expand Down
4 changes: 4 additions & 0 deletions reconciler/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ cloud.google.com/go/kms v1.25.0 h1:gVqvGGUmz0nYCmtoxWmdc1wli2L1apgP8U4fghPGSbQ=
cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk=
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 h1:Zh+Ur3OsoWpvALHPLT45nOekHkgOt+IOfutBbPqM17I=
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819/go.mod h1:WjmQxb+W6nVNCgj8nXrF24lIz95AHwnSl36tpjDZSU8=
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg=
Expand Down Expand Up @@ -218,6 +220,8 @@ github.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn
github.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0=
github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
Expand Down
34 changes: 34 additions & 0 deletions server/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,37 @@ tasks:
dir: ./server
cmds:
- go -C . test -run=^$ -bench=. ./...

server:conformance:
desc: Run OCI conformance checks
deps:
- server:start
vars:
CLONE_DIR: "/tmp/distribution-spec"
CLONE_BIN: "{{.CLONE_DIR}}/conformance/conformance.test"
env:
# Must be running already
OCI_REPORT_DIR: /tmp/results
OCI_ROOT_URL: "http://localhost:8080"
OCI_NAMESPACE: "tests/repo-a"
OCI_DEBUG: 0
OCI_TEST_PULL: 1
OCI_TEST_PUSH: 1
OCI_AUTOMATIC_CROSSMOUNT: 0 # disable automatic cross-mount
OCI_CROSSMOUNT_NAMESPACE: "tests/repo-b"
OCI_TEST_CONTENT_DISCOVERY: 1
OCI_TEST_CONTENT_MANAGEMENT: 1
OCI_HIDE_SKIPPED_WORKFLOWS: 0
OCI_DELETE_MANIFEST_BEFORE_BLOBS: 0
cmds:
- defer: { task: server:stop }
- |
# Setup conformance test binary
if [ ! -f "{{.CLONE_BIN}}" ]; then
git clone https://github.com/opencontainers/distribution-spec {{.CLONE_DIR}}
cd {{.CLONE_DIR}}/conformance
go test -c
fi

# Run test
{{.CLONE_BIN}}
3 changes: 2 additions & 1 deletion server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ replace github.com/ThalesIgnite/crypto11 => github.com/ThalesGroup/crypto11 v1.6

require (
buf.build/gen/go/agntcy/oasf/protocolbuffers/go v1.36.11-20260311134852-004f24f91cc7.1
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819
github.com/agntcy/dir/api v1.0.0
github.com/agntcy/dir/utils v1.0.0
github.com/agntcy/oasf-sdk/pkg v1.0.1
Expand Down Expand Up @@ -129,7 +130,7 @@ require (
github.com/multiformats/go-multistream v0.6.1 // indirect
github.com/multiformats/go-varint v0.1.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pion/datachannel v1.6.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 h1:Zh+Ur3OsoWpvALHPLT45nOekHkgOt+IOfutBbPqM17I=
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819/go.mod h1:WjmQxb+W6nVNCgj8nXrF24lIz95AHwnSl36tpjDZSU8=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
Expand Down Expand Up @@ -141,6 +143,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
Expand Down
6 changes: 6 additions & 0 deletions server/routing/routing_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ package routing
import (
"context"
"errors"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -56,6 +58,10 @@ func newMockStore() *mockStore {
}
}

func (m *mockStore) Server() (http.Handler, error) {
return nil, fmt.Errorf("Server method not implemented in mockStore")
}

func (m *mockStore) Push(_ context.Context, record *corev1.Record) (*corev1.RecordRef, error) {
cid := record.GetCid()
if cid == "" {
Expand Down
31 changes: 31 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"syscall"
Expand Down Expand Up @@ -65,6 +66,7 @@ type Server struct {
health *healthcheck.Checker
grpcServer *grpc.Server
metricsServer *metrics.Server
ociServer *http.Server
}

// buildConnectionOptions creates gRPC server options for connection management.
Expand Down Expand Up @@ -222,6 +224,21 @@ func New(ctx context.Context, cfg *config.Config) (*Server, error) {
return nil, fmt.Errorf("failed to create store: %w", err)
}

// Create OCI Distribution API server
storeServerAPI, err := storeAPI.Server()
if err != nil {
return nil, fmt.Errorf("failed to create store server API: %w", err)
}

// Register HTTP handler for OCI Distribution API
handler := http.NewServeMux()
handler.Handle("/v2/", storeServerAPI)
ociServer := &http.Server{
Addr: ":8080",
Handler: handler,
ReadHeaderTimeout: 3 * time.Second, //nolint:mnd
}

routingAPI, err := routing.New(ctx, storeAPI, options)
if err != nil {
return nil, fmt.Errorf("failed to create routing: %w", err)
Expand Down Expand Up @@ -314,6 +331,7 @@ func New(ctx context.Context, cfg *config.Config) (*Server, error) {
health: healthChecker,
grpcServer: grpcServer,
metricsServer: metricsServer,
ociServer: ociServer,
}, nil
}

Expand Down Expand Up @@ -385,6 +403,10 @@ func (s Server) Close(ctx context.Context) {
}
}

// Stop OCI server
_ = s.ociServer.Close() //nolint:errcheck

// Stop gRPC server
s.grpcServer.GracefulStop()
}

Expand Down Expand Up @@ -433,5 +455,14 @@ func (s Server) start(ctx context.Context) error {
}
}()

// Serve HTTP server
go func() {
logger.Info("HTTP server starting", "address", s.Options().Config().ListenAddress)

if err := s.ociServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error("Failed to start HTTP server", "error", err)
}
}()

return nil
}
5 changes: 5 additions & 0 deletions server/store/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"

corev1 "github.com/agntcy/dir/api/core/v1"
"github.com/agntcy/dir/server/types"
Expand Down Expand Up @@ -124,6 +125,10 @@ func (s *cachedStore) IsReady(ctx context.Context) bool {
return s.source.IsReady(ctx)
}

func (s *cachedStore) Server() (http.Handler, error) {
return s.source.Server()
}

// cacheRecord stores a record in the cache.
func (s *cachedStore) cacheRecord(ctx context.Context, record *corev1.Record) error {
cid := record.GetCid()
Expand Down
6 changes: 6 additions & 0 deletions server/store/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package cache

import (
"context"
"fmt"
"net/http"
"testing"

typesv1alpha1 "buf.build/gen/go/agntcy/oasf/protocolbuffers/go/agntcy/oasf/types/v1alpha1"
Expand Down Expand Up @@ -72,6 +74,10 @@ func (m *MockStoreAPI) Delete(ctx context.Context, ref *corev1.RecordRef) error
return args.Error(0) //nolint:wrapcheck // Mock should return exact error without wrapping
}

func (m *MockStoreAPI) Server() (http.Handler, error) {
return nil, fmt.Errorf("Server method not implemented in MockStoreAPI")
}

func (m *MockStoreAPI) IsReady(ctx context.Context) bool {
return true
}
Expand Down
5 changes: 5 additions & 0 deletions server/store/eventswrap/eventswrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package eventswrap

import (
"context"
"net/http"

corev1 "github.com/agntcy/dir/api/core/v1"
"github.com/agntcy/dir/server/events"
Expand Down Expand Up @@ -126,3 +127,7 @@ func (s *eventsStore) WalkReferrers(ctx context.Context, recordCID string, refer
//nolint:wrapcheck
return referrerStore.WalkReferrers(ctx, recordCID, referrerType, walkFn)
}

func (s *eventsStore) Server() (http.Handler, error) {
return s.source.Server() //nolint:wrapcheck
}
6 changes: 6 additions & 0 deletions server/store/eventswrap/eventswrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package eventswrap

import (
"context"
"fmt"
"net/http"
"testing"

typesv1alpha1 "buf.build/gen/go/agntcy/oasf/protocolbuffers/go/agntcy/oasf/types/v1alpha1"
Expand Down Expand Up @@ -47,6 +49,10 @@ func (m *mockStore) Delete(_ context.Context, _ *corev1.RecordRef) error {
return nil
}

func (m *mockStore) Server() (http.Handler, error) {
return nil, fmt.Errorf("Server method not implemented in mockStore")
}

func (m *mockStore) IsReady(_ context.Context) bool {
return true
}
Expand Down
40 changes: 40 additions & 0 deletions server/store/oci/Untitled-1.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// AI CARD
{
"meta": {
"@specVersion": "1.0", // clarify if needed or use mediaType
"id": "did:example:agent-finance-001", // MUST, bring concerns around ID (not super useful due to redeclaration/conflicts)
"name": "Acme Finance Agent", // MAY
"description": "Executes finance workflows through multiple protocol adapters.", // MAY
"logoUrl": "https://acme-finance.com/logo.png", // MAY
"version": "v1.0.0", // MAY
"maturity": "stable", // MAY
"publisher": { // ONE OR MANY publishers? maybe authors
"id": "did:example:org-acme", // "schema: <type>:<...> or <name>"
"name": "Acme Financial Corp"
},
// where to put these, remove but document implications?!
"tags": ["finance", "trading"], // keep, remove rest, link OASF for domains/skills/capabilities
"domains": ["finance"],
"skills": ["abc", "oasf:v1:images_computer_vision/image_segmentation"],
"capabilities": {}
},
"modules": [
{
"a2a": {
// a2a card
}
},
{
"mcp": {
// mcp card
}
}
],
"links": [
{
"signature": {
// signature data
}
}
]
}
Loading
Loading