OpenLinker Core is the open-source control plane for registering, finding, and running Agents. A self-hosted deployment gets one run model across REST, SDK, MCP, and A2A calls, plus routing to public endpoints, remote MCP servers, and Agents connected from local or private networks.
Core runs independently with its own Web UI, database, and deployment policy.
Chinese documentation: README.zh-CN.md
OpenLinker Core is pre-1.0 software. The runtime model is usable, but API
details, SDK contracts, migrations, and operational defaults can still change.
Pin commits or release tags for deployments, and read CHANGELOG.md before
upgrading.
User Tokens with fine-grained permission grants are part of the open-source Core product contract for
user-initiated REST, SDK, MCP, and A2A calls. Core issues and verifies
ol_user_* locally, stores resource-aware Core grants, and exposes JWT-only
management under /api/v1/user-tokens. Hosted services can validate the same
token through Core's authenticated internal introspection endpoint.
Included:
- user authentication and JWT sessions
- fine-grained User Token permissions for user-side API and protocol calls
- Agent registry, visibility, categories, skills, and benchmarks
- Agent Tokens for self-registration and runtime access
- run creation, run state, event streams, artifacts, and messages
- direct HTTP, MCP server, and transport-neutral Runtime Worker invocation modes
- A2A JSON-RPC / HTTP+JSON surfaces, Agent Card support, and optional gRPC
- MCP HTTP entrypoints and REST fallback APIs
- private task matching, workflow, delivery, webhook, and local admin APIs
- self-hosted deployment support with Postgres and Redis
Hosted product boundary:
- hosted registration, sign-in, verification codes, account recovery, and OAuth
- service listings, service orders, order snapshots, and seller operations
- wallet balances, charges, withdrawals, and Stripe flows
- hosted Agent-market ranking and commercial dashboard composition
- managed account, token-policy, and commercial access dashboards
- official certification, recommendation, and abuse-policy internals
These services stay in the hosted product layer and are not Core dependencies.
Core retains authentication primitives, including optional OAuth support, for
self-hosted deployments. That does not make hosted account or OAuth routes
Core-owned: the hosted product terminates its own /api/v1/auth/* surface in
Cloud. Core tasks remain private matching and orchestration records; they are
not a public task-bidding marketplace.
The open-source repositories use Core as the shared registry and run control plane. Core exposes a product-neutral, protected External Execution boundary for starting an Agent or Workflow Run and synchronizing its terminal result. Cloud maps hosted orders onto that boundary; account, listing, order, seller, and all commercial semantics remain outside this repository.
flowchart LR
CoreWeb["openlinker-core-web<br/>self-hosted UI"] -->|"REST / session APIs"| Core
SDKs["openlinker-go / openlinker-js / openlinker-python<br/>client and runtime SDKs"] -->|"REST / SSE / A2A / Runtime"| Core
MCPCaller["MCP or A2A caller"] -->|"tool call / message/send"| Core
Cloud["openlinker-cloud<br/>Hosted product and order mapping"] -.->|"scoped External Execution JWT<br/>start / sync"| Core
Core["openlinker-core<br/>auth / registry / runs / events"]
Core -->|"direct_http"| HTTPAgent["Public HTTPS Agent"]
Core -->|"mcp_server"| MCPAgent["Remote MCP / JSON-RPC server"]
Core -->|"runtime<br/>WebSocket first, long polling fallback"| RuntimeWorker["SDK Runtime Worker"]
RuntimeWorker -->|"typed RuntimeContext"| Handler["Application handler"]
Core -.->|"runtime<br/>optional compatibility path"| AdapterWorker["Go SDK Runtime Worker"]
AdapterWorker --> AgentNode["Agent Node Adapter"]
AgentNode -->|"http / command / a2a / codex"| Backend["Existing agent backend"]
Prerequisites:
- Go 1.25 or newer
- Docker or a local Postgres and Redis installation
make
Prebuilt binaries are published with checksums on
GitHub Releases.
Pin a release and verify its adjacent .sha256 file before installation. The
source workflow below is the recommended path for contributors.
Start dependencies:
docker compose up -d postgres redisCreate local configuration:
cp .env.example .envSet at least these values in .env:
DATABASE_URL=postgres://dev:dev@127.0.0.1:5432/openlinker?sslmode=disable
JWT_SECRET=replace-with-32-byte-random-secret
FRONTEND_URL=http://localhost:3000
ALLOW_LOCAL_HTTP_ENDPOINTS=trueGenerate a development secret with:
openssl rand -hex 32Apply migrations and run the API:
make migrate-up
make runThe default API origin is http://localhost:8080.
Health check:
curl http://localhost:8080/healthz
curl --fail http://localhost:8080/readyz/healthz is process liveness. /readyz also verifies the persisted cluster
mode, expected live replicas, release/schema/runtime-contract agreement, and
the Redis signal dependency in HA mode. A Redis outage makes an HA instance
not ready without stopping PostgreSQL reconciliation.
After migrations are applied, Core checks whether any active admin user exists.
In local, dev, development, or test, it can create the local bootstrap
admin during normal API startup:
- Email:
admin@openlinker.ai - Display name:
OpenLinker Admin - Local-only password:
openlinker-admin
For every other ENV value, including staging and production, both
OPENLINKER_BOOTSTRAP_ADMIN_EMAIL and
OPENLINKER_BOOTSTRAP_ADMIN_PASSWORD must be explicitly set before Core checks
for an existing admin. The password must be 12–72 bytes and cannot equal the
local default; the email must not use a .local domain. Missing or unsafe
bootstrap credentials fail startup closed. If an active admin already exists,
bootstrap is skipped after validation and no password is reset.
The manual repair command remains available:
make bootstrap-adminIt accepts the same environment variables, plus -env, -email, and
-password. It is idempotent: if the configured email already exists, it
promotes that user to admin and updates the password.
Change the default password immediately after first login.
Required in normal deployments:
DATABASE_URLJWT_SECRETFRONTEND_URL
Common optional values:
REDIS_URLRUNTIME_HA_MODE— settruewhenexpected_replicasis greater than oneOPENLINKER_RELEASE_ID/OPENLINKER_GIT_SHA— injected by the image build; production rejects placeholder valuesAPI_URLOAUTH_CALLBACK_BASE_URLOAUTH_ALLOWED_FRONTEND_ORIGINSOAUTH_SESSION_SECRETGOOGLE_OAUTH_CLIENT_ID/GITHUB_OAUTH_CLIENT_ID(OAuth login)GOOGLE_OAUTH_CLIENT_SECRET/GITHUB_OAUTH_CLIENT_SECRETALLOW_LOCAL_HTTP_ENDPOINTS— settruefor local developmentRUNTIME_ENDPOINT_RUN_*— run timeout worker tuning
When no LLM is configured, task routing falls back to keyword matching. To enable LLM-assisted routing and skill benchmarks:
# Option A: any OpenAI-compatible API (self-hosters, Ollama, Azure, etc.)
LLM_OPENAI_URL=https://api.openai.com/v1
LLM_OPENAI_API_KEY=sk-...
LLM_OPENAI_MODEL=gpt-4o-mini # optional, default is gpt-4o-mini
# Option B: internal proxy (openlinker.ai cloud deployment only)
LLM_COMPLETE_URL=http://internal-llm-proxy/completeOption A takes effect when LLM_COMPLETE_URL is empty. Option B is only useful
for the private cloud deployment of openlinker.ai.
User Token issuance and verification are local Core capabilities and require no external verifier. Hosted services that add their own incremental permissions can introspect the same token through Core.
| Variable | Purpose | Self-host |
|---|---|---|
OPENLINKER_INTERNAL_TOKEN |
Protects POST /internal/user-tokens/introspect; it may also authenticate trusted private services such as an LLM proxy |
Leave empty unless exposing an internal service integration |
EXTERNAL_EXECUTION_JWT_CURRENT_PUBLIC_KEY |
Ed25519 public key used only to verify product-neutral External Execution service JWTs; replay protection fails closed through Redis | Leave empty unless a trusted server-side execution client is configured |
EXTERNAL_EXECUTION_JWT_CURRENT_KEY_ID / ISSUER / AUDIENCE |
Pins the accepted signing key, token issuer, and audience; issuer also namespaces replay protection | Must exactly match the trusted client configuration |
EXTERNAL_EXECUTION_CALLER_SERVICE_ID |
Stable business caller identity carried in the signed token, independent from issuer | Must remain exactly openlinker-cloud while migration 074 legacy rows are supported; changing it requires an explicit data rekey migration |
EXTERNAL_EXECUTION_REQUEST_BINDING_REQUIRED |
Requires every External Execution JWT to bind the final method, escaped path, and exact body digest | Keep false only for the Release A compatibility window; set true after the maximum 60-second JWT TTL, 2-second leeway, in-flight window, and zero legacy observations |
EXTERNAL_EXECUTION_JWT_NEXT_PUBLIC_KEY / NEXT_KEY_ID |
Optional second verification key for staged rotation | Set both or neither; the next kid must differ from current |
make help # list Makefile targets
make deps # download and tidy Go modules
make build # build bin/api
make run # build and run with .env
make test # go test ./... -race -cover
make fmt # gofmt and go vet
make migrate-up # initialize or verify the exact current schema
make runtime-loadtest # exercise Runtime Worker over WebSocket and long pollingRuntime cluster membership is refreshed with PostgreSQL time every five
seconds. Multi-replica deployments require RUNTIME_HA_MODE=true; all live
replicas must advertise the same release, schema checksum, and OpenLinker Runtime
contract before /readyz succeeds. The migration deliberately starts in
hard_maintenance, so it is never silently treated as a serving state.
Breaking Runtime migrations use the image-bundled runtime-cutover command.
status and preflight expose redacted JSON evidence. retire-stale-members
locks cluster membership, refuses live members or other database clients, and
removes only rows older than Runtime's canonical live window; upgrades from a
pre-Runtime schema require the explicit --runtime-uninstalled-ok no-op flag. drain,
hard-maintenance, and reopen require an explicit cluster-control CAS
version, and reopen also requires the active cutover ID. Reopen only succeeds
when the database contract, exact live replica count, release identity, schema
checksum, and Redis HA dependency agree. The admin API exposes the same status
read-only at GET /api/v1/admin/runtime/maintenance; it never changes mode.
./runtime-cutover retire-stale-members --runtime-uninstalled-ok
./runtime-cutover preflight --require-exclusive --require-no-members
./runtime-cutover status
./runtime-cutover reopen --expected-version=<version> --cutover-id=<uuid>Use the simplest reachable mode for each Agent:
direct_http: Core calls a stable HTTPS Agent endpoint.mcp_server: Core calls an existing remote HTTP JSON-RPC or MCP endpoint.runtime: Runtime Worker receives assigned runs. Its transport policy isautoby default: outbound WebSocket first, long polling when the network cannot keep the socket alive. Both transports reuse one Session, lease, ACK, resume, fence, and local spool contract.
Normal Runtime Worker setup only needs OPENLINKER_URL, the public OpenLinker
origin. The SDK reads /.well-known/openlinker.json without Runtime
credentials and obtains the dedicated mTLS origin from base_urls.runtime.
RUNTIME_MTLS_API_URL is deployment-side publication metadata, not a second
address that Agent creators need to enter.
Every assigned or claimed run must finish with exactly one terminal result.
Reliable OpenLinker Runtime authenticates every Runtime Worker with a dedicated client
certificate and a matching runtime_nodes record. Keep the client CA private
key on an operator-controlled provisioning host; never copy it into the Core
container, put it in .env, or mount it beside the serving keys. Core only
needs the CA certificate configured as RUNTIME_MTLS_CLIENT_CA_FILE.
After applying the current migrations, build the Core binary and issue a Node identity from a host that can temporarily reach Postgres:
make build
DATABASE_URL='postgres://...' ./bin/api runtime-node issue \
--ca-cert /secure/runtime-client-ca.crt \
--ca-key /secure/runtime-client-ca.key \
--display-name 'Singapore worker 01' \
--capacity 4 \
--cert-out ./node-pki/runtime-node.crt \
--key-out ./node-pki/runtime-node.keyThe CA private-key file must be owner-only (0600 or 0400) on Unix. The
output directories must already exist. The command generates an ECDSA
P-256 key and a client-auth-only certificate, registers its random serial and
SPKI SHA-256 thumbprint against the current OpenLinker Runtime contract, and then emits
an audit record as JSON. It refuses to overwrite any file. The private key is
written with mode 0600; the certificate uses 0644. --node-id is optional
and otherwise generated. --node-version defaults to
openlinker-go/runtime-worker. An Adapter that advertises another implementation,
such as Agent Node, must enroll with that implementation's exact version.
Inspect a delivered pair before installing it on a Runtime Worker:
./bin/api runtime-node inspect \
--cert ./node-pki/runtime-node.crt \
--key ./node-pki/runtime-node.key \
--ca-cert /secure/runtime-client-ca.crtPass the JSON node_id, the registered capacity, the delivered certificate and
private key, and the Runtime server trust CA into the SDK RuntimeWorker
configuration. The optional Agent Node Adapter exposes the same values through
OPENLINKER_NODE_ID, OPENLINKER_AGENT_NODE_CAPACITY,
OPENLINKER_AGENT_NODE_MTLS_CERT_FILE, OPENLINKER_AGENT_NODE_MTLS_KEY_FILE,
and OPENLINKER_AGENT_NODE_MTLS_CA_FILE. Distribute the client CA certificate
to Core only; its private key remains outside all running OpenLinker services.
Core separates caller-facing protocol bindings from callee-facing Agent
connection modes. Callers always enter Core first; Core then routes the run to
the target Agent according to connection_mode.
flowchart TB
subgraph CallerBindings["Caller-facing bindings"]
REST["REST / SDK<br/>POST /run, GET /runs/:id"]
MCP["MCP tools<br/>search_agents, run_agent, get_run"]
A2AHTTP["A2A JSON-RPC / HTTP+JSON<br/>message/send, message:send"]
A2AGRPC["A2A gRPC<br/>optional SendMessage, SubscribeToTask"]
end
Core["OpenLinker Core<br/>auth, registry, run state, events, artifacts"]
REST --> Core
MCP --> Core
A2AHTTP --> Core
A2AGRPC --> Core
subgraph CalleeModes["Callee connection modes"]
Direct["direct_http<br/>Core calls HTTPS endpoint"]
MCPServer["mcp_server<br/>Core calls remote JSON-RPC / MCP tool"]
RuntimeWorker["runtime<br/>SDK Runtime Worker"]
end
Core --> Direct
Core --> MCPServer
Core --> RuntimeWorker
Important rules:
- A2A bindings are external caller-facing transports. They are not the private Runtime Worker channel.
message/sendcreates a real Core run. Synchronous endpoints may complete immediately; runtime connectors normally return a working task first.runtimeis the Agent connection mode. WebSocket and long polling are transport choices inside the Runtime Worker, never separate Agent connection modes.- WebSocket is outbound from Runtime Worker to Core. Long polling is its fallback; both keep PostgreSQL as truth and share the same Session, lease, ACK and resume state.
/api/v1/auth/*/api/v1/me/api/v1/agents/api/v1/agent-registration/*/api/v1/agent-runtime/*(dedicated mTLS listener only; the ordinary API listener returns 404)/api/v1/runs/api/v1/runs/:id/stream/api/v1/a2a/*/api/v1/mcp/api/v1/skills/api/v1/tasks/api/v1/workflows/api/v1/delivery/*/api/v1/admin/*
The canonical Runtime contract is embedded in Core and mirrored byte-for-byte by the official SDKs; tests lock its ID, protocol version, digest and feature set.
go test ./...
go test ./... -race -coverThe parent workspace also contains cross-repository validators for SDK, runtime, and A2A flows.
- Do not log or expose plaintext Agent Tokens.
- Do not pass Agent Tokens to backend subprocesses.
- Keep
ALLOW_LOCAL_HTTP_ENDPOINTS=falsein production. - Use HTTPS for public
direct_httpandmcp_serverendpoints. - Rotate any token that was printed, committed, or shared outside the intended trust boundary.
Report vulnerabilities through SECURITY.md, not public issues.
Read CONTRIBUTING.md before opening a pull request. Keep Core independent from commercial Cloud modules and update SDK contracts or tests when changing public behavior.
- Help and issue guidance: SUPPORT.md
- Release checklist: RELEASE.md
- Notable changes: CHANGELOG.md
- Conduct expectations: CODE_OF_CONDUCT.md
Apache-2.0. See LICENSE.