Skip to content
Draft
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
9 changes: 9 additions & 0 deletions .changeset/add-actor-created-via.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@hypercerts-org/lexicon": minor
---

Add `app.certified.actor.createdVia` record lexicon for lightweight account provenance.

The record carries an optional `clientId` property holding the OAuth `client_id` URL (the client metadata document URL) that uniquely identifies the application which created the Certified account. This lets consumers distinguish records originating from, for example, test or staging environments from real ones, without requiring cryptographic platform attestations.

Like other Certified records, it includes an optional `signatures` array (`app.certified.signature.defs#list`) so the provenance marker can itself be attested.
8 changes: 8 additions & 0 deletions ERD.puml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ dataclass organization {
!endif
}

' app.certified.actor.createdVia
dataclass createdVia {
!if (SHOW_FIELDS == "true")
clientId
createdAt
!endif
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

' Contributors are represented by DIDs or human-readable strings
' therefore do not require modelling via a lexicon.
entity "contributor (DID/profile)" as contributorEntity #FFD4A3 {
Expand Down
16 changes: 16 additions & 0 deletions SCHEMAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,22 @@ A location represented as a string, e.g. coordinates or a small GeoJSON string.

---

### `app.certified.actor.createdVia`

**Description:** Records which application created this Certified account, identified by the OAuth client_id URL of that application. A lightweight provenance marker that lets consumers distinguish, for example, records originating from a test or staging environment from real ones, without requiring cryptographic platform attestations.

**Key:** `literal:self`

#### Properties

| Property | Type | Required | Description |
| ------------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `clientId` | `string` | ❌ | The OAuth `client_id` URL uniquely identifying the application that created this account. This is the client metadata document URL used during the ATProto OAuth flow (e.g. 'https://app.certified.one/oauth/client-metadata.json'). |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created. |
| `signatures` | `ref` | ❌ | Optional cryptographic signatures attesting to this record's content. |

---

### `app.certified.actor.organization`

**Description:** Extended metadata for an organization actor. Complements the base actor profile with organization-specific fields like legal structure and reference links.
Expand Down
32 changes: 32 additions & 0 deletions lexicons/app/certified/actor/createdVia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"lexicon": 1,
"id": "app.certified.actor.createdVia",
"defs": {
"main": {
"type": "record",
"description": "Records which application created this Certified account, identified by the OAuth client_id URL of that application. A lightweight provenance marker that lets consumers distinguish, for example, records originating from a test or staging environment from real ones, without requiring cryptographic platform attestations.",
"key": "literal:self",
"record": {
"type": "object",
"required": ["createdAt"],
"properties": {
"clientId": {
"type": "string",
"format": "uri",
"description": "The OAuth `client_id` URL uniquely identifying the application that created this account. This is the client metadata document URL used during the ATProto OAuth flow (e.g. 'https://app.certified.one/oauth/client-metadata.json')."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this record was originally created."
},
"signatures": {
"type": "ref",
"ref": "app.certified.signature.defs#list",
"description": "Optional cryptographic signatures attesting to this record's content."
}
}
}
}
}
}
89 changes: 89 additions & 0 deletions tests/validate-actor-created-via.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { describe, it, expect } from "vitest";
import { validate, ids } from "../generated/lexicons.js";
import * as CreatedVia from "../generated/types/app/certified/actor/createdVia.js";

describe("app.certified.actor.createdVia", () => {
it("should accept a minimal valid record (only required createdAt)", () => {
const result = CreatedVia.validateMain({
$type: ids.AppCertifiedActorCreatedVia,
createdAt: "2024-01-01T00:00:00.000Z",
});
expect(result.success).toBe(true);
});

it("should accept a record with optional clientId", () => {
const result = CreatedVia.validateMain({
$type: ids.AppCertifiedActorCreatedVia,
clientId: "https://app.certified.one/oauth/client-metadata.json",
createdAt: "2024-01-01T00:00:00.000Z",
});
expect(result.success).toBe(true);
if (result.success) {
expect(result.value.clientId).toBe(
"https://app.certified.one/oauth/client-metadata.json",
);
}
});

it("should accept a record with inline signatures", () => {
const result = CreatedVia.validateMain({
$type: ids.AppCertifiedActorCreatedVia,
clientId: "https://app.certified.one/oauth/client-metadata.json",
createdAt: "2024-01-01T00:00:00.000Z",
signatures: [
{
$type: "app.certified.signature.defs#inline",
signature: new Uint8Array([1, 2, 3]),
key: "did:plc:abc123#atproto",
},
],
});
expect(result.success).toBe(true);
});

it("should accept a record missing optional clientId", () => {
const result = validate(
{ createdAt: "2024-01-01T00:00:00.000Z" },
ids.AppCertifiedActorCreatedVia,
"main",
false,
);
expect(result.success).toBe(true);
});

it("should reject a record missing required createdAt", () => {
const result = validate(
{ clientId: "https://app.certified.one/oauth/client-metadata.json" },
ids.AppCertifiedActorCreatedVia,
"main",
false,
);
expect(result.success).toBe(false);
});

it("should reject a record with a non-uri clientId", () => {
const result = validate(
{
clientId: "not a uri",
createdAt: "2024-01-01T00:00:00.000Z",
},
ids.AppCertifiedActorCreatedVia,
"main",
false,
);
expect(result.success).toBe(false);
});

it("should reject a record with an invalid createdAt format", () => {
const result = validate(
{
clientId: "https://app.certified.one/oauth/client-metadata.json",
createdAt: "not-a-datetime",
},
ids.AppCertifiedActorCreatedVia,
"main",
false,
);
expect(result.success).toBe(false);
});
});