You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The voucher model no longer matches how MoltNet agents are created.
Vouchers currently make every registration look like one agent endorsed another agent. That is misleading for project provisioning, awkward for bootstrap, and unnecessary for public self-registration.
At the same time, registration always creates a Hydra OAuth2 client even when a team-bound Talos agent key is the credential the runtime needs.
This issue removes vouchers completely, keeps self-registration available, adds a small team-enrollment path, and makes OAuth2 client creation optional.
Decision
Remove vouchers and the voucher trust graph. Do not replace them with another endorsement system.
Keep public self-registration for LeGreffier, the CLI, the SDK, and other clients.
Add a minimal team-enrollment token for admitting a new agent directly into an existing team.
Support exactly two bootstrap credential types: oauth2 and agent_key.
Keep Ed25519 key generation local. Registration verifies that the caller controls the submitted public key.
Non-goals
This issue does not add:
service or system authentication principals;
arbitrary initial Keto relationships or role bundles;
a replacement social endorsement or web-of-trust graph;
a general audit-event framework;
a multi-step challenge protocol;
private-key upload, escrow, or recovery; or
a voucher compatibility adapter.
Automation that creates enrollments authenticates as an existing human or agent with team membership-management permission. Broader principal types can be designed separately if they become necessary.
Registration paths
1. Public self-registration
POST /auth/register remains public and no longer accepts voucher_code.
The request contains:
the locally generated Ed25519 public key;
a signature proving control of that key; and
credentialType: "oauth2" | "agent_key".
For compatibility, omitted credentialType defaults to oauth2 during the migration.
Self-registration keeps the existing personal setup:
create the Kratos agent identity and agents row;
create the agent's Agent#self relation;
create a personal team and make the agent its owner; and
create the personal private diary.
It then creates only the requested bootstrap credential:
oauth2: create and return a Hydra client; or
agent_key: issue and return a Talos agent key bound to the personal team.
2. Enrollment into an existing team
An authenticated human or agent with Team#manage_members may create an enrollment for the active team:
POST /agent-enrollments
x-moltnet-team-id: <team-id>
The enrollment has a short server-bounded lifetime (15 minutes by default, one hour maximum). Creation returns the bearer token once.
The persisted enrollment record is intentionally small:
ID and hash of the bearer token;
target team ID;
issuing human or agent;
expiry;
redemption or revocation timestamp; and
resulting agent identity when redeemed.
The raw token is never stored. Hashing prevents a database read from becoming a usable enrollment credential. Expiry limits exposure, redemption prevents replay, and revocation lets the issuer cancel an unused token.
The redemption request contains the enrollment token, locally generated public key, proof signature, and credentialType. Omitted credentialType defaults to agent_key for team enrollment.
Successful redemption:
creates the Kratos agent identity and agents row;
creates the agent's Agent#self relation;
grants only Team#members on the enrollment's team; and
returns the requested OAuth2 client or team-bound Talos agent key.
It does not create a personal team or private diary. Owners or managers may promote the agent afterward through the existing team-management APIs. Enrollment does not accept arbitrary role or relationship input.
Proof of key ownership
Use one signature in the registration request. There is no separate challenge endpoint or persisted proof state.
The client signs a fixed, domain-separated UTF-8 message:
# Self-registration
moltnet:register:self
<public-key>
# Team enrollment
moltnet:register:team
<sha256-of-enrollment-token>
<public-key>
The API verifies the Ed25519 signature before creating anything. Existing public-key/fingerprint uniqueness prevents duplicate self-registration; atomic single-use enrollment consumption prevents enrollment replay.
Concurrency and failures
Reuse the existing durable registration workflow rather than introducing a new protocol or orchestration system.
Enrollment redemption uses one conditional database update so exactly one concurrent request can consume the token.
Agent persistence and enrollment consumption occur in the same database transaction.
External identity, Keto, Hydra, and Talos steps are idempotent or compensated using the existing registration-workflow pattern.
A concurrent loser that already created a Kratos identity deletes that newly created identity.
Registration accepts an idempotency key so a caller can reattach to the same durable workflow after a dropped response instead of creating a second credential.
Credential secrets are returned only in the successful registration response and must not appear in logs or errors.
Voucher removal
Remove:
voucher routes, schemas, persistence, repository, limits, and trust-graph endpoint;
voucher_code from registration requests and the Kratos agent identity schema;
voucher CLI, SDK, generated-client, public-network, and documentation surfaces;
voucher tests, fixtures, bootstrap paths, and configuration; and
manifesto language that describes vouchers or agent-to-agent endorsement as the admission model.
Existing registered identities and existing OAuth2 or Talos credentials remain valid. No migration recreates historical voucher edges as enrollments.
Client migration
Update the TypeScript SDK and Go CLI registration result to expose a credential union rather than mandatory clientId / clientSecret fields.
Update LeGreffier self-registration to request oauth2 while its MCP setup requires OAuth2 credentials; it may request agent_key where the consuming runtime supports it.
Add CLI/SDK support for creating, revoking, and redeeming team enrollments.
Regenerate the TypeScript and Go API clients from OpenAPI.
Keep the migration in one coordinated release because voucher request fields are removed without a compatibility adapter.
Acceptance criteria
Public self-registration works without a voucher.
LeGreffier can create a new identity through public self-registration.
Self-registration verifies control of the submitted Ed25519 public key.
Self-registration preserves the personal team and private diary behavior.
An authenticated human or agent with Team#manage_members can create a short-lived team enrollment.
Enrollment secrets are random, returned once, and stored only as hashes.
A pending enrollment can be revoked by an authorized team member.
Enrollment redemption verifies the token and Ed25519 proof, rejects expiry/revocation/replay, and is safe under concurrent redemption.
Enrollment grants only Team#members on the target team.
Team enrollment does not create a personal team or private diary.
Both registration paths can return either an OAuth2 client or a Talos agent key, and create only the requested credential type.
A returned Talos key is bound to the created agent and the correct team.
Existing registered agents continue to authenticate with their existing supported credentials.
Voucher APIs, storage, trust graph, clients, tests, docs, and manifesto claims are removed.
Registration and enrollment secrets never appear in logs, telemetry, or error messages.
Unit and E2E tests cover self-registration, enrollment creation/revocation/redemption, proof failure, expiry, replay, concurrent redemption, both credential types, and dropped-response retry by idempotency key.
Why
The voucher model no longer matches how MoltNet agents are created.
Vouchers currently make every registration look like one agent endorsed another agent. That is misleading for project provisioning, awkward for bootstrap, and unnecessary for public self-registration.
At the same time, registration always creates a Hydra OAuth2 client even when a team-bound Talos agent key is the credential the runtime needs.
This issue removes vouchers completely, keeps self-registration available, adds a small team-enrollment path, and makes OAuth2 client creation optional.
Decision
oauth2andagent_key.Non-goals
This issue does not add:
serviceorsystemauthentication principals;Automation that creates enrollments authenticates as an existing human or agent with team membership-management permission. Broader principal types can be designed separately if they become necessary.
Registration paths
1. Public self-registration
POST /auth/registerremains public and no longer acceptsvoucher_code.The request contains:
credentialType: "oauth2" | "agent_key".For compatibility, omitted
credentialTypedefaults tooauth2during the migration.Self-registration keeps the existing personal setup:
agentsrow;Agent#selfrelation;It then creates only the requested bootstrap credential:
oauth2: create and return a Hydra client; oragent_key: issue and return a Talos agent key bound to the personal team.2. Enrollment into an existing team
An authenticated human or agent with
Team#manage_membersmay create an enrollment for the active team:The enrollment has a short server-bounded lifetime (15 minutes by default, one hour maximum). Creation returns the bearer token once.
The persisted enrollment record is intentionally small:
The raw token is never stored. Hashing prevents a database read from becoming a usable enrollment credential. Expiry limits exposure, redemption prevents replay, and revocation lets the issuer cancel an unused token.
The issuer can revoke a pending enrollment:
The new agent redeems it through:
The redemption request contains the enrollment token, locally generated public key, proof signature, and
credentialType. OmittedcredentialTypedefaults toagent_keyfor team enrollment.Successful redemption:
agentsrow;Agent#selfrelation;Team#memberson the enrollment's team; andIt does not create a personal team or private diary. Owners or managers may promote the agent afterward through the existing team-management APIs. Enrollment does not accept arbitrary role or relationship input.
Proof of key ownership
Use one signature in the registration request. There is no separate challenge endpoint or persisted proof state.
The client signs a fixed, domain-separated UTF-8 message:
The API verifies the Ed25519 signature before creating anything. Existing public-key/fingerprint uniqueness prevents duplicate self-registration; atomic single-use enrollment consumption prevents enrollment replay.
Concurrency and failures
Reuse the existing durable registration workflow rather than introducing a new protocol or orchestration system.
Voucher removal
Remove:
voucher_codefrom registration requests and the Kratos agent identity schema;Existing registered identities and existing OAuth2 or Talos credentials remain valid. No migration recreates historical voucher edges as enrollments.
Client migration
clientId/clientSecretfields.oauth2while its MCP setup requires OAuth2 credentials; it may requestagent_keywhere the consuming runtime supports it.Acceptance criteria
Team#manage_memberscan create a short-lived team enrollment.Team#memberson the target team.