diff --git a/apps/logicsrc-web/contract/spec-discovery.contract.test.ts b/apps/logicsrc-web/contract/spec-discovery.contract.test.ts
index fc70a617..a771bd1e 100644
--- a/apps/logicsrc-web/contract/spec-discovery.contract.test.ts
+++ b/apps/logicsrc-web/contract/spec-discovery.contract.test.ts
@@ -9,6 +9,7 @@ import sitemap from "../src/app/sitemap";
vi.mock("../src/lib/supabase", () => ({ publicClient: () => { throw new Error("offline"); } }));
describe.each([
+ { slug: "openagent", name: "OpenAgent", family: "people" },
{ slug: "openabtest", name: "OpenABTest", family: "process" },
{ slug: "openfleet", name: "OpenFleet", family: "process" },
{ slug: "openrental", name: "OpenRental", family: "catalogs" },
diff --git a/apps/logicsrc-web/public/examples/openagent.json b/apps/logicsrc-web/public/examples/openagent.json
new file mode 100644
index 00000000..edda8172
--- /dev/null
+++ b/apps/logicsrc-web/public/examples/openagent.json
@@ -0,0 +1,13 @@
+{
+ "type": "logicsrc.agent",
+ "version": "0.1",
+ "name": "Research agent",
+ "did": "research.coinpay",
+ "owner_did": "operator.coinpay",
+ "description": "Find primary sources and prepare a cited research brief.",
+ "skills": ["research", "source-verification"],
+ "supported_task_types": ["research-brief"],
+ "permissions_requested": ["web:read"],
+ "polling_mode": true,
+ "public": true
+}
diff --git a/apps/logicsrc-web/src/app/openagent/page.tsx b/apps/logicsrc-web/src/app/openagent/page.tsx
new file mode 100644
index 00000000..404c85b7
--- /dev/null
+++ b/apps/logicsrc-web/src/app/openagent/page.tsx
@@ -0,0 +1,66 @@
+import Link from "next/link";
+import type { Metadata } from "next";
+import { SiteShell } from "@/components/site-shell";
+import profile from "../../../public/examples/openagent.json";
+import { mono, pre, table, td } from "../openontology/ui";
+
+export const metadata: Metadata = {
+ title: "OpenAgent · LogicSRC",
+ description: "A portable agent profile: identity, owner, skills and requested permissions in one JSON file, with bindings to OpenProfile, OpenFleet and OpenRental.",
+ alternates: { canonical: "/openagent" }
+};
+
+const LINKS = [
+ ["OpenProfile.md", "/openprofile", "The identity, accounts and topics people read. Link the JSON descriptor from the same profile page."],
+ ["OpenFleet", "/openfleet", "The running sessions and their human sysop. A launcher maps one agent profile to many fleet members."],
+ ["OpenRental", "/docs/openrental", "Agent listings and explicit rental offers. An openagent member points at this profile and matches its did."],
+ ["OpenAccess", "/openaccess", "The delegated grant. Requested permissions in a profile do not grant access."],
+ ["OpenCreds", "/opencreds", "The vault for credentials. The public profile contains no secrets."]
+];
+
+export default function OpenAgentPage() {
+ return (
+
+
+
+
People and agents · specification 0.1
+
OpenAgent
+
One file says who an agent is, who owns it, what it can do and which permissions it requests.
+
+
A directory, a job board and a launcher can read the same JSON profile. The agent keeps its identity when its hosting or engine changes, and each running session can be tracked separately through OpenFleet.
+
The format is the existing logicsrc.agent contract. Its schema and offline validator already ship in LogicSRC; this publication adds discovery conventions and explains how the profile fits the other specifications.
An illustrative research agent. Identity and ownership are claims for the consuming application to verify.
+
+
{JSON.stringify(profile, null, 2)}
+
Seven required fields: type, version, name, did, owner_did, skills and permissions_requested. Skills must be nonempty. A permission request can be empty, and never authorizes execution by itself.
+
+
+
+
+
Serve it, link it, validate it
+
+
Serve one JSON profile over HTTPS. A domain for one agent can use /.well-known/openagent.json; a host with several agents gives each its own URL. Link the descriptor from its profile page:
+
{''}
+
Try the existing validator on the downloadable example:
Validation checks the shape. Ownership, grants, availability and settlement are checked by the application using the profile. The specification documents the current identity syntax and the SDK runtime-summary distinction.
+
+
+
+
How the pieces fit
+
+ {LINKS.map(([name, href, description]) => (
+
{name}
{description}
+ ))}
+
+
OpenSwarm describes peer-to-peer file distribution. An agent profile and a file swarm keep their own identities when they appear together in a rental listing.
OpenAgent: the durable agent profile; one identity can have many running fleet members
Specification: the terms, the record, the
environment, claiming and deriving, the ledger's eight events, five verbs, fifteen
diff --git a/apps/logicsrc-web/src/lib/specs.ts b/apps/logicsrc-web/src/lib/specs.ts
index a7ff32a4..a4f81617 100644
--- a/apps/logicsrc-web/src/lib/specs.ts
+++ b/apps/logicsrc-web/src/lib/specs.ts
@@ -51,6 +51,7 @@ export const FAMILIES: Family[] = [
"One Markdown file for a person or an agent, served from their own domain and linked from every platform that has a page for them. The profile carries the identity, the accounts and the topics; the sections carry what a platform needs to match on, so a job board, a booking site or a dating app reads the file instead of asking forty questions again.",
specs: [
s("openprofile", "OpenProfile.md", "One Markdown file for who you are and where you are, people and agents alike", { status: "0.2" }),
+ s("openagent", "OpenAgent", "A portable agent profile: identity, owner, skills and requested permissions", { status: "0.1" }),
s("openwall", "OpenWall", "Consent-based broadcasts and direct messages across contact networks, with an AT Protocol mapping", { landing: undefined, status: "draft" }),
s("openresume", "OpenResume.md", "What you have done, in the same spirit, linked from the profile", { landing: undefined }),
s("openjob", "OpenJob", "What the work is, so a candidate's agent and a job board agree", { landing: undefined }),
diff --git a/docs/openagent.md b/docs/openagent.md
new file mode 100644
index 00000000..c47d7710
--- /dev/null
+++ b/docs/openagent.md
@@ -0,0 +1,134 @@
+# OpenAgent
+
+OpenAgent is a portable description of an agent: its identity, the owner responsible for it, the skills it advertises and the permissions it requests. A directory, a job board or a launcher can read the same JSON file. The agent keeps its identity when its hosting, listing or engine changes.
+
+Status: **0.1**. First publication of the existing LogicSRC agent profile contract, with publication and discovery conventions. The JSON Schema and offline validator already ship in LogicSRC. Network discovery and runtime integration are conventions for implementers; this specification does not claim that every engine implements them.
+
+Slug: `openagent`
+
+## The record
+
+An OpenAgent document is a `logicsrc.agent` profile. Version 0.1 uses the existing [agent JSON Schema](https://github.com/profullstack/logicsrc/blob/master/packages/schemas/schemas/logicsrc-agent.schema.json) without a second envelope or a new identity namespace.
+
+The following is an illustrative agent, not a live service or a paid offer. [Download this example](/examples/openagent.json).
+
+```json
+{
+ "type": "logicsrc.agent",
+ "version": "0.1",
+ "name": "Research agent",
+ "did": "research.coinpay",
+ "owner_did": "operator.coinpay",
+ "description": "Find primary sources and prepare a cited research brief.",
+ "skills": ["research", "source-verification"],
+ "supported_task_types": ["research-brief"],
+ "permissions_requested": ["web:read"],
+ "polling_mode": true,
+ "public": true
+}
+```
+
+Seven fields are required: `type`, `version`, `name`, `did`, `owner_did`, `skills` and `permissions_requested`. Optional fields may be omitted; omission is never proof of availability, a free price or permission to run the agent.
+
+| Field | Meaning and constraints |
+| --- | --- |
+| `type` | Exactly `logicsrc.agent` |
+| `version` | `0.1` for this publication; the underlying schema accepts numeric major/minor versions with an optional patch |
+| `name` | A display name, 1 to 120 characters; identity comparisons use `did`, not this name |
+| `did` | The stable LogicSRC identity of the agent, for example `research.coinpay` |
+| `owner_did` | The LogicSRC identity of the party responsible for the agent |
+| `skills` | At least one unique, nonempty string describing an advertised specialty; a claim, not a certification |
+| `permissions_requested` | Unique scope strings such as `web:read` or `repo:write`; an empty array is valid and requests nothing |
+| `description` | Optional plain text explaining what the agent does |
+| `supported_task_types` | Optional unique, nonempty task labels; the host defines their contracts |
+| `pricing` | Optional indicative pricing: `model` (`free`, `per_task`, `hourly` or `subscription`), nonnegative numeric `amount` and `currency` (2 to 12 characters); the schema permits partial objects, which readers must show as incomplete |
+| `webhook_url` | Optional absolute URI for an application-specific callback; this field does not define a transport, authentication method or task API |
+| `polling_mode` | Optional boolean indicating that the agent can poll; it does not specify where or how often |
+| `public` | Optional boolean, defaulting to true in the schema; publishers must still enforce access controls for private profiles |
+| `logicsrc_compatibility_version` | Optional text naming the LogicSRC version the publisher claims to support |
+
+The schema disallows extra properties. Put platform-specific data in a separate record linked by the host, rather than adding fields that existing validators reject. A reader must not silently rewrite an unsupported version to `0.1`.
+
+### Identity and ownership
+
+In this version, both identity fields follow the existing LogicSRC pattern `^[a-z0-9][a-z0-9._-]*\.[a-z0-9][a-z0-9._-]*$`. These are LogicSRC names such as `research.coinpay`; a W3C identifier such as `did:key:...` does not match this schema. They are not URLs. Readers must not infer a resolver or fetch an address by appending the identity to a hostname.
+
+A profile states an ownership claim. Passing schema validation or serving the file over HTTPS does not prove control of either identity. A registry or launcher verifies the claim through its own authenticated account or identity provider before granting control. The owner's identifier is kept distinct from the human sysop of a running OpenFleet session; publishing a profile never appoints a fleet sysop.
+
+### Skills and permissions
+
+Skill strings are portable labels, not executable instructions or an agreed global taxonomy. A consumer can match labels it understands and show the rest as supplied. It must not invent a supported task contract from a skill name.
+
+Permission strings follow the existing schema: a lowercase resource name, optionally followed by one colon and a lowercase action. Digits, dots, underscores and hyphens are allowed after the first letter in each part. Wildcards are not part of this profile format.
+
+`permissions_requested` records a request. The application's actual grant, local policy and the session's inherited limits decide what may run. Reading a descriptor, seeing a skill or accepting a listing grants no access. Credentials belong in [OpenCreds](/opencreds), and delegated authorization can use [OpenAccess](/openaccess).
+
+## Publication and discovery
+
+Serve the JSON record over HTTPS with `Content-Type: application/json`. A domain representing one agent should serve it at `/.well-known/openagent.json`. A host with several agents serves one record per stable HTTPS URL, for example `/agents/research.json`, and links each from its own profile page or directory entry. The well-known document is a single profile, not an array or a directory.
+
+A page about an agent can advertise the descriptor with:
+
+```html
+
+```
+
+An HTTP response can carry the equivalent `Link: ; rel="openagent"; type="application/json"` header. A reader may also be handed the descriptor URL directly. Relative discovery links resolve against the page or response URL. HTTPS links may point at another host; readers retain that source and verify ownership separately.
+
+A reader fetches the descriptor, validates it, checks the supported version and compares `did` with any identity supplied by the listing. A mismatch is an error, not an automatic rename. Keep the source URL and retrieval time beside the cached record; use HTTP validators such as ETag or Last-Modified when the publisher provides them. Two URLs with the same `did` are two sources about one agent, and conflicting ownership claims must be surfaced rather than silently combined.
+
+Public files must contain no access tokens, private keys or private workspace paths. `public: false` is descriptive metadata, not protection for a file served without authentication. A directory must not publicly index such a record. Readers apply their own URL and network policies to descriptors, redirects and callbacks; importing a profile must not automatically call its webhook or launch a process.
+
+## One profile, many sessions
+
+An OpenAgent profile describes the durable agent. [OpenFleet](/openfleet) describes each running session: its human sysop, parent, engine, task and ceiling. One agent can have many sessions and each gets its own fleet member id.
+
+OpenFleet 0.1 does not define an OpenAgent identity field. An integrating launcher keeps a mapping between the profile's `did` and the fleet's `(fleet, member)` pair in its own state. It must not treat the profile identity as a unique session id or add invented fields to the fleet contract. The profile's requested permissions never widen a fleet ceiling. Existing fleet hooks continue to work without an OpenAgent profile.
+
+An agent orchestration swarm is a set of sessions in OpenFleet. [OpenSwarm](/openswarm) retains its peer-to-peer file-distribution meaning. An agent may operate a peer, but its profile is not a file swarm manifest.
+
+## Profiles and rental listings
+
+[OpenProfile.md](/openprofile) is the human-readable identity, accounts and topics for a person or an agent. OpenAgent supplies the machine-readable capability and permission contract. A host can link both from the same profile page; neither format replaces the other or proves the other's ownership claims by itself.
+
+[OpenRental](/docs/openrental) already names agent members with `kind: "openagent"`. The member's `id` is the profile's `did`, and its HTTPS `url` points at this JSON document. A listing must check that the returned identity matches. Membership does not authorize execution or create a rental offer.
+
+The profile's optional `pricing` is an indication, not a checkout contract. Explicit rental offers, their exact decimal-string rates and CoinPay settlement metadata belong in OpenRental. A consumer must not turn the profile's JSON number into a binding payment or charge an account merely because it fetched the file.
+
+## Use it today
+
+Download the sample and validate it with the existing LogicSRC validator CLI:
+
+```sh
+curl -fsS https://logicsrc.com/examples/openagent.json -o agent.json
+npx --yes --package @logicsrc/validators logicsrc-validate agent agent.json
+```
+
+The same schema is exported as `@logicsrc/schemas/agent`. In a JavaScript application using `@logicsrc/validators`:
+
+```js
+import { validate } from "@logicsrc/validators";
+
+const result = validate("agent", profile);
+if (!result.ok) throw new Error(JSON.stringify(result.errors));
+```
+
+Schema validation checks the document's shape. The consumer separately checks the version, ownership, grants, current availability and any offer before running or paying an agent. The existing SDK's `LogicSrcAgent` interface is a runtime summary with `id` and `capabilities`; it is not this wire document and must not be serialized as an OpenAgent profile without an explicit mapping.
+
+## Conformance
+
+A **publisher** serves a schema-valid `logicsrc.agent` document, retains a stable `did`, names an owner and at least one skill, and distinguishes requested permissions from granted access. A public publisher exposes an HTTPS descriptor URL and advertises it through one of the discovery mechanisms above.
+
+A **reader** validates the shape and supported version, preserves the source URL, checks expected identity matches, treats owner and skill claims as unverified until checked, and keeps discovery separate from execution and payment. A reader shows unsupported or incomplete fields as such instead of assuming defaults that the contract does not provide.
+
+A **launcher** using the profile applies its own authorization and runtime policy. If it implements OpenFleet, it records each session under that specification and retains the profile-to-session mapping without weakening the inherited ceiling.
+
+## Version history
+
+| Version | Date | Change |
+| --- | --- | --- |
+| 0.1 | 2026-09-13 | Names the existing LogicSRC agent profile as OpenAgent; adds publication, discovery, conformance and bindings to OpenProfile, OpenFleet and OpenRental |
+
+## License
+
+The specification text is CC BY 4.0. The existing schema and implementation retain their repository licenses.
diff --git a/docs/openfleet.md b/docs/openfleet.md
index 6a62269b..25635500 100644
--- a/docs/openfleet.md
+++ b/docs/openfleet.md
@@ -228,6 +228,7 @@ Three ship: `logicsrc fleet` in `@logicsrc/openfleet` 0.1.0 (logicsrc CLI 0.3.0)
## Related standards
+- [OpenAgent](/openagent): the durable agent profile, with its identity, owner, skills and requested permissions. A launcher can map one profile to many fleet members; the profile does not change a session's sysop or ceiling.
- [OpenSwarm](/openswarm): unrelated and easy to confuse. Peer-to-peer file and media distribution; its swarm is a set of peers. Each spec carries one line pointing at the other.
- [OpenAccess](/openaccess): `ceiling.budget` borrows its money limit string and adds ` tokens`. A delegated grant is the network-side twin of a ceiling, a child narrower than its parent and revoked with it. A fleet API, if one comes, is an OpenAccess app with `fleet:read` and `fleet:control` scopes.
- [OpenProfile.md](/openprofile): the sysop, where the human has one.
diff --git a/docs/openrental.md b/docs/openrental.md
index 51d2b3bc..3741716e 100644
--- a/docs/openrental.md
+++ b/docs/openrental.md
@@ -15,7 +15,7 @@ Each member has a `kind`, stable `id` and HTTPS `url`. The pair `(kind, id)` is
| `openagent` | The `did` of a LogicSRC agent profile, such as `analyst.coinpay` | A document conforming to the existing [`logicsrc.agent` schema](https://github.com/profullstack/logicsrc/blob/master/packages/schemas/schemas/logicsrc-agent.schema.json); its `did` MUST equal the member `id` |
| `openswarm` | `ed25519:` followed by the 64 lowercase hex characters of the swarm's file public key | The signed `ipfile.manifest` for that file, served by its publisher or an HTTP gateway; its `file` key MUST equal the member `id` |
-**OpenAgent binding:** this draft uses the existing LogicSRC agent profile as its OpenAgent representation. It does not introduce a second profile format. Agent identities, listing `owner_did` and rental `payee_did` use the existing LogicSRC DID syntax (`name.coinpay`, for example), rather than a new identity namespace.
+**OpenAgent binding:** [OpenAgent 0.1](/docs/openagent) names the existing LogicSRC agent profile as its representation and defines how to publish and discover it. It does not introduce a second profile format. Agent identities, listing `owner_did` and rental `payee_did` use the existing LogicSRC DID syntax (`name.coinpay`, for example), rather than a new identity namespace.
**OpenSwarm binding:** [OpenSwarm](/docs/openswarm) retains its meaning as the paid, encrypted peer-to-peer distribution family. A member is an actual `ipfile` swarm identified by its stable file key, not a content-version hash, publisher key, hub, or AgentSwarm orchestration session. The manifest may change under that key. AgentSwarm's runtime can consume a listing as application input, but a runtime session is not implicitly an `openswarm` member. The `url` is an HTTP view of the existing manifest, not a new OpenSwarm endpoint requirement.