Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions apps/logicsrc-web/contract/spec-discovery.contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
13 changes: 13 additions & 0 deletions apps/logicsrc-web/public/examples/openagent.json
Original file line number Diff line number Diff line change
@@ -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
}
66 changes: 66 additions & 0 deletions apps/logicsrc-web/src/app/openagent/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<SiteShell active="OpenAgent">
<section className="band">
<div className="section-head">
<p className="eyebrow">People and agents · specification 0.1</p>
<h2>OpenAgent</h2>
<p>One file says who an agent is, who owns it, what it can do and which permissions it requests.</p>
</div>
<p>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.</p>
<p>The format is the existing <code style={mono}>logicsrc.agent</code> contract. Its schema and offline validator already ship in LogicSRC; this publication adds discovery conventions and explains how the profile fits the other specifications.</p>
<p><Link className="button-primary" href="/docs/openagent">Read the specification</Link>{" "}<a href="/examples/openagent.json">Download the example</a></p>
</section>

<section className="band">
<div className="section-head">
<h2>The profile</h2>
<p>An illustrative research agent. Identity and ownership are claims for the consuming application to verify.</p>
</div>
<pre style={pre}>{JSON.stringify(profile, null, 2)}</pre>
<p>Seven required fields: <code style={mono}>type</code>, <code style={mono}>version</code>, <code style={mono}>name</code>, <code style={mono}>did</code>, <code style={mono}>owner_did</code>, <code style={mono}>skills</code> and <code style={mono}>permissions_requested</code>. Skills must be nonempty. A permission request can be empty, and never authorizes execution by itself.</p>
</section>

<section className="band">
<div className="section-head">
<h2>Serve it, link it, validate it</h2>
</div>
<p>Serve one JSON profile over HTTPS. A domain for one agent can use <code style={mono}>/.well-known/openagent.json</code>; a host with several agents gives each its own URL. Link the descriptor from its profile page:</p>
<pre style={pre}>{'<link rel="openagent" type="application/json" href="https://example.com/agents/research.json">'}</pre>
<p>Try the existing validator on the downloadable example:</p>
<pre style={pre}>{"curl -fsS https://logicsrc.com/examples/openagent.json -o agent.json\nnpx --yes --package @logicsrc/validators logicsrc-validate agent agent.json"}</pre>
<p>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.</p>
</section>

<section className="band">
<div className="section-head"><h2>How the pieces fit</h2></div>
<table style={table}>
<tbody>{LINKS.map(([name, href, description]) => (
<tr key={name}><td style={td}><Link href={href}>{name}</Link></td><td style={td}>{description}</td></tr>
))}</tbody>
</table>
<p><Link href="/openswarm">OpenSwarm</Link> 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.</p>
</section>
</SiteShell>
);
}
1 change: 1 addition & 0 deletions apps/logicsrc-web/src/app/openfleet/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export default function OpenFleetPage(): ReactNode {
<h2>Where everything lives</h2>
</div>
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
<li><Link href="/openagent">OpenAgent</Link>: the durable agent profile; one identity can have many running fleet members</li>
<li>
<Link href="/docs/openfleet">Specification</Link>: the terms, the record, the
environment, claiming and deriving, the ledger&apos;s eight events, five verbs, fifteen
Expand Down
1 change: 1 addition & 0 deletions apps/logicsrc-web/src/lib/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }),
Expand Down
134 changes: 134 additions & 0 deletions docs/openagent.md
Original file line number Diff line number Diff line change
@@ -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
<link rel="openagent" type="application/json" href="https://example.com/agents/research.json">
```

An HTTP response can carry the equivalent `Link: <https://example.com/agents/research.json>; 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.
1 change: 1 addition & 0 deletions docs/openfleet.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<n> 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.
Expand Down
Loading
Loading