OpenLinker Core
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
Status
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.
Scope
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.
Open-source Architecture
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"]
Quick Start
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 redis
Create local configuration:
cp .env.example .env
Set 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=true
Generate a development secret with:
openssl rand -hex 32
Apply migrations and run the API:
make migrate-up
make run
The 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.
Initial Admin Bootstrap
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-admin
It 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.
Configuration
Required in normal deployments:
DATABASE_URL
JWT_SECRET
FRONTEND_URL
Common optional values:
REDIS_URL
RUNTIME_HA_MODE — set true when expected_replicas is greater than one
OPENLINKER_RELEASE_ID / OPENLINKER_GIT_SHA — injected by the image build;
production rejects placeholder values
API_URL
OAUTH_CALLBACK_BASE_URL
OAUTH_ALLOWED_FRONTEND_ORIGINS
OAUTH_SESSION_SECRET
GOOGLE_OAUTH_CLIENT_ID / GITHUB_OAUTH_CLIENT_ID (OAuth login)
GOOGLE_OAUTH_CLIENT_SECRET / GITHUB_OAUTH_CLIENT_SECRET
ALLOW_LOCAL_HTTP_ENDPOINTS — set true for local development
RUNTIME_ENDPOINT_RUN_* — run timeout worker tuning
LLM configuration (optional, for task routing and benchmarks)
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/complete
Option A takes effect when LLM_COMPLETE_URL is empty. Option B is only useful
for the private cloud deployment of openlinker.ai.
User Token introspection and private-service variables
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 |
Common Commands
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 polling
Runtime Modes
Runtime 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 is
auto by 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.
Runtime Node certificate provisioning
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.key
The 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.crt
Pass 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.
Invocation Architecture
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/send creates a real Core run. Synchronous endpoints may complete
immediately; runtime connectors normally return a working task first.
runtime is 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 Areas
/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.
Testing
go test ./...
go test ./... -race -cover
The parent workspace also contains cross-repository validators for SDK,
runtime, and A2A flows.
Security
- Do not log or expose plaintext Agent Tokens.
- Do not pass Agent Tokens to backend subprocesses.
- Keep
ALLOW_LOCAL_HTTP_ENDPOINTS=false in production.
- Use HTTPS for public
direct_http and mcp_server endpoints.
- Rotate any token that was printed, committed, or shared outside the intended
trust boundary.
Report vulnerabilities through SECURITY.md, not public issues.
Contributing
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.
Support and Releases
License
Apache-2.0. See LICENSE.