diff --git a/.vscode/settings.recommended.json b/.vscode/settings.recommended.json
index c152c545fd..c63dff43ce 100644
--- a/.vscode/settings.recommended.json
+++ b/.vscode/settings.recommended.json
@@ -1,5 +1,5 @@
{
- // Recommended `settings.json` configuration
+ "$comment": "Recommended `settings.json` configuration",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
@@ -37,12 +37,20 @@
"rust",
"rust/c509-certificate",
"rust/cardano-chain-follower",
+ "rust/catalyst-types",
"rust/catalyst-voting",
+ "rust/immutable-ledger",
+ "rust/vote-tx-v1",
+ "rust/vote-tx-v2",
+ "rust/signed-doc",
"rust/cbork",
"rust/hermes-ipfs",
+ "rust/rbac-registration",
+ "rust/cardano-blockchain-types",
"dart",
"docs",
- "general"
+ "general",
+ "deps"
],
"conventionalCommits.gitmoji": false,
"markdown.extension.toc.unorderedList.marker": "*",
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/all.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/all.dot
index d22921c2f9..4e18a642f2 100644
--- a/docs/src/architecture/08_concepts/signed_doc/diagrams/all.dot
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/all.dot
@@ -1,4 +1,4 @@
-digraph "None" {
+digraph "All" {
rankdir="LR"
graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
@@ -448,6 +448,126 @@ digraph "None" {
];
+ "Profile Template" [
+ id="Profile Template";
+ label=<
+
+
+
+ Profile Template
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ 0ce8ab38-9258-4fbc-a62e-7faa6e58318f 1b70f611-518d-479e-be73-11b5e9cb68a5 |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Profile" [
+ id="Profile";
+ label=<
+
+
+
+ Profile
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ 1b70f611-518d-479e-be73-11b5e9cb68a5 |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ template |
+ Profile Template |
+
+
+ |
+
+
+
+ >
+ ];
+
+
"Proposal Template" [
id="Proposal Template";
label=<
@@ -963,10 +1083,346 @@ digraph "None" {
];
+ "Representative Profile" [
+ id="Representative Profile";
+ label=<
+
+
+
+ Representative Profile
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ e3f2c1b4-7890-4abc-8def-2345678901ef |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ template |
+ Representative Profile Template |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile Template" [
+ id="Representative Category Profile Template";
+ label=<
+
+
+
+ Representative Category Profile Template
+ |
+
+
+
+
+
+ content type |
+ application/schema+json |
+
+
+ |
+
+
+
+
+
+ type |
+ 0ce8ab38-9258-4fbc-a62e-7faa6e58318f f1a2b3c4-1111-4abc-8def-2345678901aa |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile" [
+ id="Representative Category Profile";
+ label=<
+
+
+
+ Representative Category Profile
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ f1a2b3c4-1111-4abc-8def-2345678901aa |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ ref |
+ Representative Profile |
+
+
+ |
+
+
+
+
+
+ template |
+ Representative Category Profile Template |
+
+
+ |
+
+
+
+
+
+ parameters |
+ Category Parameters |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Profile Template" [
+ id="Representative Profile Template";
+ label=<
+
+
+
+ Representative Profile Template
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ 0ce8ab38-9258-4fbc-a62e-7faa6e58318f e3f2c1b4-7890-4abc-8def-2345678901ef |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Voter Representative Delegation" [
+ id="Voter Representative Delegation";
+ label=<
+
+
+
+ Voter Representative Delegation
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ f1a2b3c4-3333-4abc-8def-2345678901cc |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ ref |
+ Representative Category Profile |
+
+
+ |
+
+
+
+
+
+ parameters |
+ Category Parameters |
+
+
+ |
+
+
+
+ >
+ ];
+
+
"Campaign Parameters":"parameters":e -> "Brand Parameters":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Category Parameters":"parameters":e -> "Campaign Parameters":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Comment Moderation Action":"ref":e -> "Proposal Comment":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Decision Parameters":"parameters":e -> "Brand Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+ "Profile":"template":e -> "Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Proposal":"template":e -> "Proposal Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Proposal":"parameters":e -> "Brand Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
"Proposal Comment":"ref":e -> "Proposal":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
@@ -982,4 +1438,10 @@ digraph "None" {
"Proposal Submission Action":"parameters":e -> "Brand Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
"Proposal Template":"template":e -> "Proposal Meta Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Proposal Template":"parameters":e -> "Brand Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+ "Representative Category Profile":"ref":e -> "Representative Profile":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Representative Category Profile":"template":e -> "Representative Category Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Representative Category Profile":"parameters":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+ "Representative Profile":"template":e -> "Representative Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Voter Representative Delegation":"ref":e -> "Representative Category Profile":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Voter Representative Delegation":"parameters":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/category_parameters.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/category_parameters.dot
index b6a2aa470a..020b4dbc10 100644
--- a/docs/src/architecture/08_concepts/signed_doc/diagrams/category_parameters.dot
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/category_parameters.dot
@@ -219,6 +219,36 @@ Relationships"
];
+ "Representative Category Profile" [
+ id="Representative Category Profile";
+ label=<
+
+
+
+ Representative Category Profile
+ |
+
+
+
+ >
+ ];
+
+
+ "Voter Representative Delegation" [
+ id="Voter Representative Delegation";
+ label=<
+
+
+
+ Voter Representative Delegation
+ |
+
+
+
+ >
+ ];
+
+
"Category Parameters":"parameters":e -> "Campaign Parameters":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
"Decision Parameters":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
"Proposal":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
@@ -228,4 +258,6 @@ Relationships"
"Proposal Meta Template":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
"Proposal Submission Action":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
"Proposal Template":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+ "Representative Category Profile":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+ "Voter Representative Delegation":"title":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/profile.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/profile.dot
new file mode 100644
index 0000000000..791767ab27
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/profile.dot
@@ -0,0 +1,96 @@
+digraph "Profile" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Profile Document Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Profile Template" [
+ id="Profile Template";
+ label=<
+
+
+
+ Profile Template
+ |
+
+
+
+ >
+ ];
+
+
+ "Profile" [
+ id="Profile";
+ label=<
+
+
+
+ Profile
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ 1b70f611-518d-479e-be73-11b5e9cb68a5 |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ template |
+ Profile Template |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Profile":"template":e -> "Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/profile_template.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/profile_template.dot
new file mode 100644
index 0000000000..68a6aeffda
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/profile_template.dot
@@ -0,0 +1,87 @@
+digraph "Profile Template" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Profile Template Document
+Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Profile Template" [
+ id="Profile Template";
+ label=<
+
+
+
+ Profile Template
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ 0ce8ab38-9258-4fbc-a62e-7faa6e58318f 1b70f611-518d-479e-be73-11b5e9cb68a5 |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Profile" [
+ id="Profile";
+ label=<
+
+ >
+ ];
+
+
+ "Profile":"title":e -> "Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_category_profile.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_category_profile.dot
new file mode 100644
index 0000000000..2eb5e6b20d
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_category_profile.dot
@@ -0,0 +1,171 @@
+digraph "Representative Category Profile" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Representative Category
+Profile Document Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Representative Profile" [
+ id="Representative Profile";
+ label=<
+
+
+
+ Representative Profile
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile Template" [
+ id="Representative Category Profile Template";
+ label=<
+
+
+
+ Representative Category Profile Template
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile" [
+ id="Representative Category Profile";
+ label=<
+
+
+
+ Representative Category Profile
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ f1a2b3c4-1111-4abc-8def-2345678901aa |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ ref |
+ Representative Profile |
+
+
+ |
+
+
+
+
+
+ template |
+ Representative Category Profile Template |
+
+
+ |
+
+
+
+
+
+ parameters |
+ Category Parameters |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Voter Representative Delegation" [
+ id="Voter Representative Delegation";
+ label=<
+
+
+
+ Voter Representative Delegation
+ |
+
+
+
+ >
+ ];
+
+
+ subgraph cluster_system_parameters {
+ label = "System Parameters";
+ color=blue
+ penwidth=20
+
+ "Category Parameters" [
+ id="Category Parameters";
+ label=<
+
+
+
+ Category Parameters
+ |
+
+
+
+ >
+ ];
+
+ }
+
+ "Representative Category Profile":"ref":e -> "Representative Profile":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Representative Category Profile":"template":e -> "Representative Category Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Representative Category Profile":"parameters":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+ "Voter Representative Delegation":"title":e -> "Representative Category Profile":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_category_profile_template.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_category_profile_template.dot
new file mode 100644
index 0000000000..6d45bac8cd
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_category_profile_template.dot
@@ -0,0 +1,88 @@
+digraph "Representative Category Profile Template" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Representative Category
+Profile Template Document
+Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Representative Category Profile Template" [
+ id="Representative Category Profile Template";
+ label=<
+
+
+
+ Representative Category Profile Template
+ |
+
+
+
+
+
+ content type |
+ application/schema+json |
+
+
+ |
+
+
+
+
+
+ type |
+ 0ce8ab38-9258-4fbc-a62e-7faa6e58318f f1a2b3c4-1111-4abc-8def-2345678901aa |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile" [
+ id="Representative Category Profile";
+ label=<
+
+
+
+ Representative Category Profile
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile":"title":e -> "Representative Category Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_profile.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_profile.dot
new file mode 100644
index 0000000000..4a8e4e9428
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_profile.dot
@@ -0,0 +1,113 @@
+digraph "Representative Profile" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Representative Profile
+Document Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Representative Profile Template" [
+ id="Representative Profile Template";
+ label=<
+
+
+
+ Representative Profile Template
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Profile" [
+ id="Representative Profile";
+ label=<
+
+
+
+ Representative Profile
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ e3f2c1b4-7890-4abc-8def-2345678901ef |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ template |
+ Representative Profile Template |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Category Profile" [
+ id="Representative Category Profile";
+ label=<
+
+
+
+ Representative Category Profile
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Profile":"template":e -> "Representative Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Representative Category Profile":"title":e -> "Representative Profile":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_profile_template.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_profile_template.dot
new file mode 100644
index 0000000000..3484aff65e
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/representative_profile_template.dot
@@ -0,0 +1,88 @@
+digraph "Representative Profile Template" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Representative Profile
+Template Document
+Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Representative Profile Template" [
+ id="Representative Profile Template";
+ label=<
+
+
+
+ Representative Profile Template
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ 0ce8ab38-9258-4fbc-a62e-7faa6e58318f e3f2c1b4-7890-4abc-8def-2345678901ef |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Profile" [
+ id="Representative Profile";
+ label=<
+
+
+
+ Representative Profile
+ |
+
+
+
+ >
+ ];
+
+
+ "Representative Profile":"title":e -> "Representative Profile Template":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/diagrams/voter_representative_delegation.dot b/docs/src/architecture/08_concepts/signed_doc/diagrams/voter_representative_delegation.dot
new file mode 100644
index 0000000000..2c7dbd319b
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/diagrams/voter_representative_delegation.dot
@@ -0,0 +1,130 @@
+digraph "Voter Representative Delegation" {
+ rankdir="LR"
+ graph [fontname="helvetica", fontsize="32", fontcolor="#29235c", bgcolor="white"];
+ node [penwidth="0", margin="0", fontname="helvetica", fontsize="32", fontcolor="#29235c"];
+ edge [fontname="helvetica", fontsize="32", fontcolor="red", color="#29235c"];
+
+ labelloc="t"
+ label="Voter Representative
+Delegation Document
+Relationships"
+ fontcolor="#1d71b8"
+ fontsize=50
+ compound=true
+
+
+
+ "Representative Category Profile" [
+ id="Representative Category Profile";
+ label=<
+
+
+
+ Representative Category Profile
+ |
+
+
+
+ >
+ ];
+
+
+ "Voter Representative Delegation" [
+ id="Voter Representative Delegation";
+ label=<
+
+
+
+ Voter Representative Delegation
+ |
+
+
+
+
+
+ content type |
+ application/json |
+
+
+ |
+
+
+
+
+
+ type |
+ f1a2b3c4-3333-4abc-8def-2345678901cc |
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ ver |
+ Document Ver |
+
+
+ |
+
+
+
+
+
+ ref |
+ Representative Category Profile |
+
+
+ |
+
+
+
+
+
+ parameters |
+ Category Parameters |
+
+
+ |
+
+
+
+ >
+ ];
+
+
+ subgraph cluster_system_parameters {
+ label = "System Parameters";
+ color=blue
+ penwidth=20
+
+ "Category Parameters" [
+ id="Category Parameters";
+ label=<
+
+
+
+ Category Parameters
+ |
+
+
+
+ >
+ ];
+
+ }
+
+ "Voter Representative Delegation":"ref":e -> "Representative Category Profile":"title":w [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*"]
+ "Voter Representative Delegation":"parameters":e -> "Category Parameters" [dir=forward, penwidth=6, color="#29235c", headlabel="1", taillabel="*", lhead="cluster_system_parameters"]
+}
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/brand_parameters.md b/docs/src/architecture/08_concepts/signed_doc/docs/brand_parameters.md
index 6e74a9b972..8f2571f9e8 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/brand_parameters.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/brand_parameters.md
@@ -112,7 +112,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/campaign_parameters.md b/docs/src/architecture/08_concepts/signed_doc/docs/campaign_parameters.md
index 40b40909c1..8836c1999a 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/campaign_parameters.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/campaign_parameters.md
@@ -130,7 +130,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/category_parameters.md b/docs/src/architecture/08_concepts/signed_doc/docs/category_parameters.md
index 07f5156139..7f123e98fc 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/category_parameters.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/category_parameters.md
@@ -130,7 +130,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/comment_moderation_action.md b/docs/src/architecture/08_concepts/signed_doc/docs/comment_moderation_action.md
index 5b9907ca1f..e8bd4caf85 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/comment_moderation_action.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/comment_moderation_action.md
@@ -153,7 +153,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/decision_parameters.md b/docs/src/architecture/08_concepts/signed_doc/docs/decision_parameters.md
index 95e049ecf2..dc128901ba 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/decision_parameters.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/decision_parameters.md
@@ -132,7 +132,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/profile.md b/docs/src/architecture/08_concepts/signed_doc/docs/profile.md
new file mode 100644
index 0000000000..3ccd3d0273
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/profile.md
@@ -0,0 +1,142 @@
+# Profile
+
+## Description
+
+## Profile Document
+
+A minimal user profile that provides basic information about a user.
+Its structure is defined by the referenced Profile Template.
+It is used as a base for more specific profiles like the Representative Profile.
+
+
+
+```graphviz dot profile.dot.svg
+{{ include_file('./../diagrams/profile.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+* The signer must be a registered 'User'.
+* The payload must be valid against the [JSON schema] defined in the referenced 'Profile Template'.
+
+### Business Logic
+
+#### Front End
+
+* Display the user's profile information.
+* Allow a user to edit their own profile data.
+
+#### Back End
+
+* Validate and store profile data against the referenced 'Profile_Template'.
+* This profile serves as the base document for a user.
+ Its scope can be extended to create more specific profiles.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `1b70f611-518d-479e-be73-11b5e9cb68a5` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+### [`template`](../metadata.md#template)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Profile Template](profile_template.md) |
+
+Reference to the template used to create and/or validate this document.
+
+#### [`template`](../metadata.md#template) Validation
+
+In addition to the validation performed for [Document Reference](../metadata.md#document-reference) type fields,
+The document payload is not valid if it does not validate completely against the referenced template.
+
+## Payload
+
+The profile payload contains all base profile fields.
+Its structure is defined by the referenced Profile Template.
+
+## Signers
+
+The following user roles may sign documents of this type:
+
+* Registered
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+* First Published Version
+
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[JSON Schema]: https://json-schema.org/draft-07
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/profile_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/profile_template.md
new file mode 100644
index 0000000000..b23af06e27
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/profile_template.md
@@ -0,0 +1,168 @@
+# Profile Template
+
+## Description
+
+## Profile Template Document
+
+Defines the allowed payload contents and constraints for a generic user profile.
+
+
+
+```graphviz dot profile_template.dot.svg
+{{ include_file('./../diagrams/profile_template.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+* The signer MUST be a registered 'Admin'.
+* The payload MUST be a valid [JSON schema].
+* The schema SHOULD define a minimal set of profile fields (e.g., name, bio).
+
+### Business Logic
+
+#### Front End
+
+
+
+#### Back End
+
+* Validate and store the [JSON schema] that defines the structure for all 'Profile' documents.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `0ce8ab38-9258-4fbc-a62e-7faa6e58318f`,
`1b70f611-518d-479e-be73-11b5e9cb68a5` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+## Payload
+
+[JSON Schema] document which defines the valid contents of a profile document.
+
+### Schema
+
+
+??? abstract
+
+ [JSON Schema] document which defines the valid contents of a profile document.
+
+ ```json
+ {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/profile_template.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "description": "Schema for a profile document template for any Catalyst actor.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "bio": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "bio"
+ ],
+ "title": "Profile Template Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
+ ```
+
+
+
+## Signers
+
+The following admin roles may sign documents of this type:
+
+* Brand Admin
+
+The following user roles may sign documents of this type:
+
+* Registered
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+ * First Published Version
+
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[JSON Schema]: https://json-schema.org/draft-07
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal.md
index c11ad6354e..70bffb1365 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal.md
@@ -226,7 +226,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment.md
index 212b50e7b4..984dd48cf3 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment.md
@@ -261,7 +261,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_meta_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_meta_template.md
index 305e9d497f..73280ae4f7 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_meta_template.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_meta_template.md
@@ -141,7 +141,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_template.md
index 91c7b241f9..ff64f26852 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_template.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_comment_template.md
@@ -154,7 +154,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_meta_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_meta_template.md
index 9ff14e8810..dd91bf8185 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_meta_template.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_meta_template.md
@@ -141,7 +141,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_moderation_action.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_moderation_action.md
index b8236d4597..8792ac4368 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_moderation_action.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_moderation_action.md
@@ -153,7 +153,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_submission_action.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_submission_action.md
index 68d7f89c2e..e15b76b4be 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_submission_action.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_submission_action.md
@@ -199,49 +199,106 @@ States:
`hide` is only actioned if sent by the author,
for a collaborator it identified that they do not wish to be listed as a `collaborator`.
-Schema :
-
-```json
-{
- "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/proposal_submission_action.schema.json",
- "$schema": "http://json-schema.org/draft-07/schema#",
- "additionalProperties": false,
- "definitions": {
- "action": {
- "description": "The action being performed on the Proposal.",
- "enum": [
- "final",
- "draft",
- "hide"
+### Schema
+
+
+??? abstract
+
+ The kind of action is controlled by this payload.
+ The Payload is a [JSON][RFC8259] Document, and must conform to this schema.
+
+ States:
+
+ * `final` : All collaborators must publish a `final` status for the proposal to be `final`.
+ * `draft` : Reverses the previous `final` state for a signer and accepts collaborator status to a document.
+ * `hide` : Requests the proposal be hidden (not final, but a hidden draft).
+ `hide` is only actioned if sent by the author,
+ for a collaborator it identified that they do not wish to be listed as a `collaborator`.
+
+ ```json
+ {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/proposal_submission_action.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "definitions": {
+ "action": {
+ "description": "The action being performed on the Proposal.",
+ "enum": [
+ "final",
+ "draft",
+ "hide"
+ ],
+ "type": "string"
+ }
+ },
+ "description": "Structure of the payload of a Proposal Submission Action.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
],
- "type": "string"
+ "properties": {
+ "action": {
+ "$ref": "#/definitions/action"
+ }
+ },
+ "required": [
+ "action"
+ ],
+ "title": "Proposal Submission Action Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-03-01": [
+ "First Version Created."
+ ]
+ }
+ }
+ ```
+
+
+
+### Examples
+
+??? example "Example: Final Proposal Submission"
+
+ This document indicates the linked proposal is final and requested to proceed for further consideration.
+
+ ```json
+ {
+ "action": "final"
}
- },
- "description": "Structure of the payload of a Proposal Submission Action.",
- "maintainers": [
+ ```
+
+
+
+??? example "Example: Draft Proposal Submission"
+
+ This document indicates the linked proposal is no longer final and should not proceed for further consideration.
+ It is also used by collaborators to accept that they are a collaborator on a document.
+
+ ```json
{
- "name": "Catalyst Team",
- "url": "https://projectcatalyst.io/"
+ "action": "draft"
}
- ],
- "properties": {
- "action": {
- "$ref": "#/definitions/action"
+ ```
+
+
+
+??? example "Example: Hidden Proposal Submission"
+
+ If submitted by the proposal author the document is hidden, it is still public but not shown as
+ a proposal being drafted.
+ If submitted by a collaborator, that collaborator is declaring they do not wish to be listed as
+ a collaborator on the proposal.
+
+ ```json
+ {
+ "action": "hide"
}
- },
- "required": [
- "action"
- ],
- "title": "Proposal Submission Action Payload Schema",
- "type": "object",
- "x-changelog": {
- "2025-03-01": [
- "First Version Created."
- ]
- }
-}
-```
-
+ ```
+
+
## Signers
@@ -260,7 +317,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_template.md
index b01b6eb41c..53ac87aff1 100644
--- a/docs/src/architecture/08_concepts/signed_doc/docs/proposal_template.md
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/proposal_template.md
@@ -158,7 +158,7 @@ New versions of this document may be published by:
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/representative_category_profile.md b/docs/src/architecture/08_concepts/signed_doc/docs/representative_category_profile.md
new file mode 100644
index 0000000000..f6c354e38d
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/representative_category_profile.md
@@ -0,0 +1,210 @@
+# Representative Category Profile
+
+## Description
+
+## Representative Category Profile Document
+
+A Representative Category Profile is created to opt in as a Representative for a specific campaign category, the user must have registered as a Representative.
+The presence of this document signifies the user's intent to participate in that category as a Representative.
+
+The document's structure is defined by the associated Representative_Category_Profile_Template, which allows an Admin to specify category-specific requirements.
+
+The payload must contain a 'status' field to indicate if the Representative is active or has revoked their participation.
+
+
+
+```graphviz dot representative_category_profile.dot.svg
+{{ include_file('./../diagrams/representative_category_profile.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+* The signer MUST be a registered 'Representative'.
+* The 'ref' metadata field MUST point to a valid 'Representative Profile' document.
+* The 'parameters' metadata field MUST point to a valid 'Category Parameters' document.
+* The 'template' metadata field MUST point to a valid 'Representative Category Profile Template' document.
+* The payload MUST be valid against the [JSON schema] defined in the referenced template.
+
+### Business Logic
+
+#### Front End
+
+* Allows a Representative to create or update their profile for a category.
+* The Representative sets their status to 'active' to be discoverable for delegation.
+* The Representative can set their status to 'revoked' to signal they are no longer participating in the category, without having to revoke the document.
+
+#### Back End
+
+* The backend MUST verify the signer is a 'Representative' and that all referenced documents exist.
+* The system will only consider Representatives with an 'active' status as eligible for delegation.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `f1a2b3c4-1111-4abc-8def-2345678901aa` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+### [`ref`](../metadata.md#ref)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Representative Profile](representative_profile.md) |
+
+Reference to a Linked Document or Documents.
+This is the primary hierarchical reference to a related document.
+
+If a reference is defined as required, there must be at least 1 reference specified.
+Some documents allow multiple references, and they are documented as required.
+
+The document reference serves two purposes:
+
+1. It ensures that the document referenced by an ID/Version is not substituted.
+ In other words, that the document intended to be referenced, is actually referenced.
+2. It Allows the document to be unambiguously located in decentralized storage systems.
+
+There can be any number of Document Locations in any reference.
+The currently defined locations are:
+
+* `cid` : A [CBOR Encoded IPLD Content Identifier][CBOR-TAG-42] ( AKA an [IPFS CID][IPFS-CID] ).
+* Others may be added when further storage mechanisms are defined.
+
+The document location does not guarantee that the document is actually stored.
+It only defines that if it were stored, this is the identifier
+that is required to retrieve it.
+Therefore it is required that Document References
+are unique and reproducible, given a documents contents.
+
+#### [`ref`](../metadata.md#ref) Validation
+
+The following must be true for a valid reference:
+
+* The Referenced Document **MUST** Exist
+* Every value in the `document_locator` must consistently reference the exact same document.
+* The `document_id` and `document_ver` **MUST** match the values in the referenced document.
+
+### [`template`](../metadata.md#template)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Representative Category Profile Template](representative_category_profile_template.md) |
+
+Reference to the template used to create and/or validate this document.
+
+#### [`template`](../metadata.md#template) Validation
+
+In addition to the validation performed for [Document Reference](../metadata.md#document-reference) type fields,
+The document payload is not valid if it does not validate completely against the referenced template.
+
+### [`parameters`](../metadata.md#parameters)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Category Parameters](category_parameters.md) |
+
+A reference to the Parameters Document this document lies under.
+
+#### [`parameters`](../metadata.md#parameters) Validation
+
+In addition to the validation performed for [Document Reference](../metadata.md#document-reference) type fields:
+
+* Any linked referenced document that includes a [`parameters`](../metadata.md#parameters) metadata must match the
+[`parameters`](../metadata.md#parameters) of the referencing document.
+
+## Payload
+
+The Representative's profile data for a specific category.
+Its structure is defined by the referenced template document.
+It MUST contain a 'status' field ('active' or 'revoked') to manage the Representative's participation.
+
+## Signers
+
+The following user roles may sign documents of this type:
+
+* Representative
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+* First Published Version
+
+[CBOR-TAG-42]: https://github.com/ipld/cid-cbor/
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[JSON Schema]: https://json-schema.org/draft-07
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[IPFS-CID]: https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/representative_category_profile_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/representative_category_profile_template.md
new file mode 100644
index 0000000000..13ab60679c
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/representative_category_profile_template.md
@@ -0,0 +1,177 @@
+# Representative Category Profile Template
+
+## Description
+
+## Representative Category Profile Template
+Defines the [JSON schema] for a 'Representative Category Profile'.
+This allows an 'Admin' to specify different profile requirements for each category.
+
+
+
+```graphviz dot representative_category_profile_template.dot.svg
+{{ include_file('./../diagrams/representative_category_profile_template.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+* The signer MUST be a registered 'Admin'.
+* The payload MUST be a valid [JSON schema].
+* The schema MUST include a 'status' field.
+
+### Business Logic
+
+#### Front End
+
+
+
+#### Back End
+
+* Validate and store the [JSON schema] that defines the structure for 'Representative Category Profile' documents.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/schema+json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `0ce8ab38-9258-4fbc-a62e-7faa6e58318f`,
`f1a2b3c4-1111-4abc-8def-2345678901aa` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+## Payload
+
+[JSON Schema] document which defines the valid contents of a Representative Category Profile document.
+The schema MUST include a 'status' field to indicate if the Representative is active or withdrawn from the category.
+
+### Schema
+
+
+??? abstract
+
+ [JSON Schema] document which defines the valid contents of a Representative Category Profile document.
+ The schema MUST include a 'status' field to indicate if the Representative is active or withdrawn from the category.
+
+ ```json
+ {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/representative_category_profile_template.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": true,
+ "definitions": {
+ "status": {
+ "description": "The Representative's status in this category. 'active' means they are participating, 'revoked' means they have withdrawn.",
+ "enum": [
+ "active",
+ "revoked"
+ ],
+ "type": "string"
+ }
+ },
+ "description": "This schema is defined by an Admin to specify the required properties for a user opting in as a Representative within a specific campaign category. It outlines the structure of a Representative's profile for that category and supports the addition of custom properties as needed by the Admin. The status field is mandatory and cannot be removed or modified by the Admin.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "status": {
+ "$ref": "#/definitions/status"
+ }
+ },
+ "required": [
+ "status"
+ ],
+ "title": "Representative Category Profile Template Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
+ ```
+
+
+
+## Signers
+
+The following admin roles may sign documents of this type:
+
+* Brand Admin
+* Campaign Admin
+
+The following user roles may sign documents of this type:
+
+* Registered
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+ * First Published Version
+
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[JSON Schema]: https://json-schema.org/draft-07
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/representative_profile.md b/docs/src/architecture/08_concepts/signed_doc/docs/representative_profile.md
new file mode 100644
index 0000000000..aaaf8c9434
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/representative_profile.md
@@ -0,0 +1,139 @@
+# Representative Profile
+
+## Description
+
+## Representative Profile Document
+
+A Representative-specific profile, extending the minimal profile with Representative-specific fields.
+
+
+
+```graphviz dot representative_profile.dot.svg
+{{ include_file('./../diagrams/representative_profile.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+ * The signer MUST be a registered 'Representative'.
+ * The payload MUST be valid against the [JSON schema] defined in the referenced 'Representative Profile Template'.
+
+### Business Logic
+
+#### Front End
+
+* Display and allow editing of the Representative's core profile fields.
+* This profile serves as the central hub for a Representative's identity.
+
+#### Back End
+
+* Validate Representative profile data against the referenced 'Representative_Profile_Template' and store it.
+* This global profile is the foundational document referenced by all of the Representative's category-specific profiles.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `e3f2c1b4-7890-4abc-8def-2345678901ef` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+### [`template`](../metadata.md#template)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Representative Profile Template](representative_profile_template.md) |
+
+Reference to the template used to create and/or validate this document.
+
+#### [`template`](../metadata.md#template) Validation
+
+In addition to the validation performed for [Document Reference](../metadata.md#document-reference) type fields,
+The document payload is not valid if it does not validate completely against the referenced template.
+
+## Payload
+
+The Representative profile payload contains all base profile fields and Representative-specific fields.
+Its structure is defined by the referenced Representative Profile Template.
+
+## Signers
+
+The following user roles may sign documents of this type:
+
+* Representative
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+* First Published Version
+
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[JSON Schema]: https://json-schema.org/draft-07
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/representative_profile_template.md b/docs/src/architecture/08_concepts/signed_doc/docs/representative_profile_template.md
new file mode 100644
index 0000000000..b1d3a64a23
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/representative_profile_template.md
@@ -0,0 +1,195 @@
+# Representative Profile Template
+
+## Description
+
+## Representative Profile Template
+Defines the [JSON schema] for a 'Representative Profile'.
+This template allows an 'Admin' to enforce a specific structure and set of constraints for Representative profiles.
+
+
+
+```graphviz dot representative_profile_template.dot.svg
+{{ include_file('./../diagrams/representative_profile_template.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+* The signer MUST be a registered 'Admin'.
+* The payload MUST be a valid [JSON schema].
+
+### Business Logic
+
+#### Front End
+
+
+
+#### Back End
+
+* Validate and store the [JSON schema] that defines the structure for all 'Representative Profile' documents.
+* The schema MUST extend the base 'Profile' schema with Representative-specific fields.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `0ce8ab38-9258-4fbc-a62e-7faa6e58318f`,
`e3f2c1b4-7890-4abc-8def-2345678901ef` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+## Payload
+
+[JSON Schema] document which defines the valid contents of a Representative profile document.
+
+### Schema
+
+
+??? abstract
+
+ [JSON Schema] document which defines the valid contents of a Representative profile document.
+
+ ```json
+ {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/drep_profile_template.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "description": "Schema for a Representative profile document template, extending the base profile with Representative-specific fields as defined by the Admin.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "avatar": {
+ "$ref": "common_definitions.schema.json#/definitions/ipfsUrl"
+ },
+ "avatar_required": {
+ "enum": [
+ "yes",
+ "optional",
+ "excluded"
+ ],
+ "type": "string"
+ },
+ "bio": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "social_links": {
+ "items": {
+ "$ref": "common_definitions.schema.json#/definitions/url"
+ },
+ "type": "array"
+ },
+ "tags": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "website": {
+ "$ref": "common_definitions.schema.json#/definitions/url"
+ }
+ },
+ "required": [
+ "name",
+ "bio",
+ "avatar",
+ "website",
+ "social_links",
+ "tags",
+ "avatar_required"
+ ],
+ "title": "Representative Profile Template Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
+ ```
+
+
+
+## Signers
+
+The following user roles may sign documents of this type:
+
+* Registered
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+ * First Published Version
+
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[JSON Schema]: https://json-schema.org/draft-07
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/docs/voter_representative_delegation.md b/docs/src/architecture/08_concepts/signed_doc/docs/voter_representative_delegation.md
new file mode 100644
index 0000000000..168e8a839e
--- /dev/null
+++ b/docs/src/architecture/08_concepts/signed_doc/docs/voter_representative_delegation.md
@@ -0,0 +1,230 @@
+# Voter Representative Delegation
+
+## Description
+
+## Voter Representative Delegation
+A signed document that allows a 'Voter' to delegate their voting power to a 'Representative' for a specific category.
+
+
+
+```graphviz dot voter_representative_delegation.dot.svg
+{{ include_file('./../diagrams/voter_representative_delegation.dot', indent=4) }}
+```
+
+
+
+### Validation
+
+* The signer MUST be a registered 'Voter'.
+* The 'ref' metadata field MUST point to a valid 'Representative Category Profile'.
+* The payload MUST be empty.
+
+### Business Logic
+
+#### Front End
+
+* Allows a voter to select a Representative from a list of eligible candidates for a category.
+* The voter signs this document to confirm their delegation choice.
+
+#### Back End
+
+* Verifies that the voter and Representative are valid and registered for the category.
+* Records the delegation of voting power from the voter to the Representative.
+
+## [COSE Header Parameters][RFC9052-HeaderParameters]
+
+* [content type](../spec.md#content-type) = `application/json`
+* [content-encoding](../spec.md#content-encoding) = `[br]`
+
+## Metadata
+
+### [`type`](../metadata.md#type)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Type](../metadata.md#document-type) |
+| Type | `f1a2b3c4-3333-4abc-8def-2345678901cc` |
+
+The document TYPE.
+
+#### [`type`](../metadata.md#type) Validation
+
+**MUST** be a known document type.
+
+### [`id`](../metadata.md#id)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Id](../metadata.md#document-id) |
+
+Document ID, created the first time the document is created.
+This must be a properly created [UUIDv7][RFC9562-V7] which contains the
+timestamp of when the document was created.
+
+#### [`id`](../metadata.md#id) Validation
+
+IF [`ver`](../metadata.md#ver) does not == [`id`](../metadata.md#id) then a document with
+[`id`](../metadata.md#id) and [`ver`](../metadata.md#ver) being equal *MUST* exist.
+
+### [`ver`](../metadata.md#ver)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Ver](../metadata.md#document-ver) |
+
+The unique version of the document.
+The first version of the document must set [`ver`](../metadata.md#ver) == [`id`](../metadata.md#id)
+
+#### [`ver`](../metadata.md#ver) Validation
+
+The document version must always be >= the document ID.
+
+### [`ref`](../metadata.md#ref)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Representative Category Profile](representative_category_profile.md) |
+
+Reference to a Linked Document or Documents.
+This is the primary hierarchical reference to a related document.
+
+If a reference is defined as required, there must be at least 1 reference specified.
+Some documents allow multiple references, and they are documented as required.
+
+The document reference serves two purposes:
+
+1. It ensures that the document referenced by an ID/Version is not substituted.
+ In other words, that the document intended to be referenced, is actually referenced.
+2. It Allows the document to be unambiguously located in decentralized storage systems.
+
+There can be any number of Document Locations in any reference.
+The currently defined locations are:
+
+* `cid` : A [CBOR Encoded IPLD Content Identifier][CBOR-TAG-42] ( AKA an [IPFS CID][IPFS-CID] ).
+* Others may be added when further storage mechanisms are defined.
+
+The document location does not guarantee that the document is actually stored.
+It only defines that if it were stored, this is the identifier
+that is required to retrieve it.
+Therefore it is required that Document References
+are unique and reproducible, given a documents contents.
+
+#### [`ref`](../metadata.md#ref) Validation
+
+The following must be true for a valid reference:
+
+* The Referenced Document **MUST** Exist
+* Every value in the `document_locator` must consistently reference the exact same document.
+* The `document_id` and `document_ver` **MUST** match the values in the referenced document.
+
+### [`parameters`](../metadata.md#parameters)
+
+
+| Parameter | Value |
+| --- | --- |
+| Required | yes |
+| Format | [Document Reference](../metadata.md#document-reference) |
+| Valid References | [Category Parameters](category_parameters.md) |
+
+A reference to the Parameters Document this document lies under.
+
+#### [`parameters`](../metadata.md#parameters) Validation
+
+In addition to the validation performed for [Document Reference](../metadata.md#document-reference) type fields:
+
+* Any linked referenced document that includes a [`parameters`](../metadata.md#parameters) metadata must match the
+[`parameters`](../metadata.md#parameters) of the referencing document.
+
+## Payload
+
+A minimal payload indicating the intended status of the delegation.
+ 'active' creates or affirms the delegation.
+ 'revoked' withdraws the delegation.
+
+### Schema
+
+
+??? abstract
+
+ A minimal payload indicating the intended status of the delegation.
+ 'active' creates or affirms the delegation.
+ 'revoked' withdraws the delegation.
+
+ ```json
+ {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/voter_representative_delegation.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "description": "This payload is submitted when a voter delegates to a Representative, with the voter as the signer and the Representative\u2019s category profile as the referenced document; to revoke the delegation, the voter can publish again with the status set to revoked.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "status": {
+ "description": "The status of the delegation. 'active' signifies delegation (set by default), 'revoked' signifies withdrawal of delegation.",
+ "enum": [
+ "active",
+ "revoked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "status"
+ ],
+ "title": "Voter Representative Delegation Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
+ ```
+
+
+
+## Signers
+
+The following user roles may sign documents of this type:
+
+* Registered
+
+New versions of this document may be published by:
+
+* author
+
+## Copyright
+
+| Copyright | :copyright: 2024-2025 IOG Singapore, All Rights Reserved |
+| --- | --- |
+| License | This document is licensed under [CC-BY-4.0] |
+| Created | 2024-12-27 |
+| Modified | 2025-06-19 |
+| Authors | Alex Pozhylenkov |
+| | Neil McAuliffe |
+| | Steven Johnson |
+
+### Changelog
+
+#### 0.01 (2025-06-19)
+
+* First Published Version
+
+[CBOR-TAG-42]: https://github.com/ipld/cid-cbor/
+[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
+[CC-BY-4.0]: https://creativecommons.org/licenses/by/4.0/legalcode
+[IPFS-CID]: https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid
+[RFC9562-V7]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
diff --git a/docs/src/architecture/08_concepts/signed_doc/metadata.md b/docs/src/architecture/08_concepts/signed_doc/metadata.md
index 7b6fdc2e13..a4310bfac4 100644
--- a/docs/src/architecture/08_concepts/signed_doc/metadata.md
+++ b/docs/src/architecture/08_concepts/signed_doc/metadata.md
@@ -208,6 +208,13 @@ The document version must always be >= the document ID.
| | [Campaign Parameters](./docs/campaign_parameters.md) |
| | [Category Parameters](./docs/category_parameters.md) |
| | [Decision Parameters](./docs/decision_parameters.md) |
+| | [Profile](./docs/profile.md) |
+| | [Profile Template](./docs/profile_template.md) |
+| | [Representative Profile Template](./docs/representative_profile_template.md) |
+| | [Representative Category Profile Template](./docs/representative_category_profile_template.md) |
+| | [Representative Profile](./docs/representative_profile.md) |
+| | [Representative Category Profile](./docs/representative_category_profile.md) |
+| | [Voter Representative Delegation](./docs/voter_representative_delegation.md) |
Reference to a Linked Document or Documents.
This is the primary hierarchical reference to a related document.
@@ -252,6 +259,9 @@ The following must be true for a valid reference:
| | [Proposal Template](./docs/proposal_template.md) |
| | [Proposal Comment Meta Template](./docs/proposal_comment_meta_template.md) |
| | [Proposal Comment Template](./docs/proposal_comment_template.md) |
+| | [Profile Template](./docs/profile_template.md) |
+| | [Representative Profile Template](./docs/representative_profile_template.md) |
+| | [Representative Category Profile Template](./docs/representative_category_profile_template.md) |
Reference to the template used to create and/or validate this document.
@@ -348,7 +358,7 @@ In addition to the validation performed for [Document Reference](metadata.md#doc
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/docs/src/architecture/08_concepts/signed_doc/spec.md b/docs/src/architecture/08_concepts/signed_doc/spec.md
index f2cc3a81bb..222eff96a0 100644
--- a/docs/src/architecture/08_concepts/signed_doc/spec.md
+++ b/docs/src/architecture/08_concepts/signed_doc/spec.md
@@ -25,6 +25,40 @@ Catalyst Signed Documents are based on [COSE][RFC9052].
Specifically, the [COSE Sign][RFC9052-CoseSign] format is used.
This allows one or more signatures to be attached to the same document.
+While every Catalyst Signed Document is a valid [COSE Sign][RFC9052-CoseSign] format document,
+not every [COSE Sign][RFC9052-CoseSign] format document is a valid Catalyst Signed Document.
+The following restrictions apply:
+
+### Unprotected Headers are not permitted
+
+It is a requirement that any document that contains exactly the same data, must produce the same
+catalyst signed document.
+This means that unprotected headers, which do not form part of the data protected by
+the signature are not permitted.
+Any document which contains any unprotected headers is not a valid Catalyst Signed Document,
+even though it may be a valid [COSE Sign][RFC9052-CoseSign] formatted document.
+
+### Only defined metadata and [COSE][RFC9052] Headers are allowed
+
+Each document type, defines a set of metadata and the [COSE][RFC9052] Headers which are allowed in that document type.
+Even if the Catalyst Signed document metadata exists in this specification, IF it is not defined as
+a valid metadata or [COSE][RFC9052] Header field for that particular document it may not be present.
+Unexpected but otherwise valid Metadata or [COSE][RFC9052] Header fields invalidate the Catalyst Signed Document.
+
+### No undefined metadata or unused [COSE][RFC9052] Headers may be present
+
+[COSE][RFC9052] Header Fields which are defined by the [COSE][RFC9052] Specification, but are NOT defined as part of a
+Catalyst Signed Document may not be present.
+Any such [COSE][RFC9052] Header Fields present in the document render it an invalid Catalyst Signed Document.
+
+Any metadata field that is not defined in this specification may not be present in any protected header.
+Unrecognized metadata fields in a document render it an invalid Catalyst Signed Document.
+
+### [CBOR Deterministic Encoding][CBOR-LFD-ENCODING] MUST be used
+
+The Catalyst Signed Document **MUST** be encoded using [CBOR Deterministic Encoding][CBOR-LFD-ENCODING].
+The "length-first core deterministic encoding requirements" variant of deterministic encoding *MUST* be used.
+
### Signed Document [CDDL][RFC8610] Definition
@@ -120,7 +154,7 @@ used to sign the protected portion of the document.
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
@@ -138,6 +172,15 @@ used to sign the protected portion of the document.
* Use generalized parameters.
+#### 0.04 (2025-05-30)
+
+* Improve and make document serialization more repeatable, and stricter.
+* TODO: Define Systems parameters
+* TODO: Define DReps documents.
+* TODO: Define Proposer Profiles.
+* TODO: Define Role 0 Profile.
+
+[CBOR-LFD-ENCODING]: https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.3
[application/schema+json]: https://datatracker.ietf.org/doc/draft-bhutton-json-schema/
[RFC9052-HeaderParameters]: https://www.rfc-editor.org/rfc/rfc8152#section-3.1
[application/cbor]: https://www.iana.org/assignments/media-types/application/cbor
diff --git a/docs/src/architecture/08_concepts/signed_doc/types.md b/docs/src/architecture/08_concepts/signed_doc/types.md
index c9b878b78e..008e5b57f4 100644
--- a/docs/src/architecture/08_concepts/signed_doc/types.md
+++ b/docs/src/architecture/08_concepts/signed_doc/types.md
@@ -13,9 +13,13 @@ All Document Types are defined by composing these base document types:
| Comment | `b679ded3-0e7c-41ba-89f8-da62a17898ea` | `37(h'b679ded30e7c41ba89f8da62a17898ea')` |
| Decision | `788ff4c6-d65a-451f-bb33-575fe056b411` | `37(h'788ff4c6d65a451fbb33575fe056b411')` |
| ModerationAction | `a5d232b8-5e03-4117-9afd-be32b878fcdd` | `37(h'a5d232b85e0341179afdbe32b878fcdd')` |
+| Profile | `1b70f611-518d-479e-be73-11b5e9cb68a5` | `37(h'1b70f611518d479ebe7311b5e9cb68a5')` |
| Proposal | `7808d2ba-d511-40af-84e8-c0d1625fdfdc` | `37(h'7808d2bad51140af84e8c0d1625fdfdc')` |
+| RepresentativeCategoryProfile | `f1a2b3c4-1111-4abc-8def-2345678901aa` | `37(h'f1a2b3c411114abc8def2345678901aa')` |
+| RepresentativeProfile | `e3f2c1b4-7890-4abc-8def-2345678901ef` | `37(h'e3f2c1b478904abc8def2345678901ef')` |
| SubmissionAction | `78927329-cfd9-4ea1-9c71-0e019b126a65` | `37(h'78927329cfd94ea19c710e019b126a65')` |
| Template | `0ce8ab38-9258-4fbc-a62e-7faa6e58318f` | `37(h'0ce8ab3892584fbca62e7faa6e58318f')` |
+| VoterRepresentativeDelegation | `f1a2b3c4-3333-4abc-8def-2345678901cc` | `37(h'f1a2b3c433334abc8def2345678901cc')` |
## Document Types
@@ -29,6 +33,8 @@ All Defined Document Types
| [Category Parameters](./docs/category_parameters.md) | Category | [37(h'818938c331394daaafe6974c78488e95')] |
| [Comment Moderation Action](./docs/comment_moderation_action.md) | Action/Comment/ModerationAction | [37(h'5e60e623ad024a1ba1ac406db978ee48'),
37(h'b679ded30e7c41ba89f8da62a17898ea'),
37(h'a5d232b85e0341179afdbe32b878fcdd')] |
| [Decision Parameters](./docs/decision_parameters.md) | Decision | [37(h'788ff4c6d65a451fbb33575fe056b411')] |
+| [Profile](./docs/profile.md) | Profile | [37(h'1b70f611518d479ebe7311b5e9cb68a5')] |
+| [Profile Template](./docs/profile_template.md) | Template/Profile | [37(h'0ce8ab3892584fbca62e7faa6e58318f'),
37(h'1b70f611518d479ebe7311b5e9cb68a5')] |
| [Proposal](./docs/proposal.md) | Proposal | [37(h'7808d2bad51140af84e8c0d1625fdfdc')] |
| [Proposal Comment](./docs/proposal_comment.md) | Comment/Proposal | [37(h'b679ded30e7c41ba89f8da62a17898ea'),
37(h'7808d2bad51140af84e8c0d1625fdfdc')] |
| [Proposal Comment Meta Template](./docs/proposal_comment_meta_template.md) | Template/Template/Comment/Proposal | [37(h'0ce8ab3892584fbca62e7faa6e58318f'),
37(h'0ce8ab3892584fbca62e7faa6e58318f'),
37(h'b679ded30e7c41ba89f8da62a17898ea'),
37(h'7808d2bad51140af84e8c0d1625fdfdc')] |
@@ -37,6 +43,11 @@ All Defined Document Types
| [Proposal Moderation Action](./docs/proposal_moderation_action.md) | Action/Proposal/ModerationAction | [37(h'5e60e623ad024a1ba1ac406db978ee48'),
37(h'7808d2bad51140af84e8c0d1625fdfdc'),
37(h'a5d232b85e0341179afdbe32b878fcdd')] |
| [Proposal Submission Action](./docs/proposal_submission_action.md) | Action/Proposal/SubmissionAction | [37(h'5e60e623ad024a1ba1ac406db978ee48'),
37(h'7808d2bad51140af84e8c0d1625fdfdc'),
37(h'78927329cfd94ea19c710e019b126a65')] |
| [Proposal Template](./docs/proposal_template.md) | Template/Proposal | [37(h'0ce8ab3892584fbca62e7faa6e58318f'),
37(h'7808d2bad51140af84e8c0d1625fdfdc')] |
+| [Representative Category Profile](./docs/representative_category_profile.md) | RepresentativeCategoryProfile | [37(h'f1a2b3c411114abc8def2345678901aa')] |
+| [Representative Category Profile Template](./docs/representative_category_profile_template.md) | Template/RepresentativeCategoryProfile | [37(h'0ce8ab3892584fbca62e7faa6e58318f'),
37(h'f1a2b3c411114abc8def2345678901aa')] |
+| [Representative Profile](./docs/representative_profile.md) | RepresentativeProfile | [37(h'e3f2c1b478904abc8def2345678901ef')] |
+| [Representative Profile Template](./docs/representative_profile_template.md) | Template/RepresentativeProfile | [37(h'0ce8ab3892584fbca62e7faa6e58318f'),
37(h'e3f2c1b478904abc8def2345678901ef')] |
+| [Voter Representative Delegation](./docs/voter_representative_delegation.md) | VoterRepresentativeDelegation | [37(h'f1a2b3c433334abc8def2345678901cc')] |
## Document Relationship Hierarchy
@@ -55,7 +66,7 @@ All Defined Document Types
| --- | --- |
| License | This document is licensed under [CC-BY-4.0] |
| Created | 2024-12-27 |
-| Modified | 2025-05-05 |
+| Modified | 2025-05-30 |
| Authors | Alex Pozhylenkov |
| | Steven Johnson |
diff --git a/specs/gen_docs/gen/doc_generator.py b/specs/gen_docs/gen/doc_generator.py
index f392378355..aebfc401ba 100644
--- a/specs/gen_docs/gen/doc_generator.py
+++ b/specs/gen_docs/gen/doc_generator.py
@@ -170,7 +170,7 @@ def add_reference_links(self) -> None:
actual_link_names = self._spec.link_names()
- actual_links_used = {}
+ actual_links_used: dict[str, str] = {}
for link_name in actual_link_names:
esc_link_name = re.escape(link_name)
link_name_regex = f"(^|\\s)({esc_link_name})(\\.|\\s|$)"
@@ -190,7 +190,7 @@ def add_reference_links(self) -> None:
for link, actual in actual_links_used.items():
self._filedata += f"\n[{link}]: {actual}"
- def remove_tabs(self, tabstop: int = 4) -> str:
+ def remove_tabs(self, tabstop: int = 4) -> None:
"""Replace tabs in the input text with spaces so that the text aligns on tab stops.
Args:
@@ -343,7 +343,7 @@ def file_name(self) -> str:
"""Return the files name."""
return self._filename
- def file_path(self, relative_doc: typing.Self | None = None) -> Path:
+ def file_path(self, relative_doc: "DocGenerator | None" = None) -> Path:
"""Return a path to the file."""
if relative_doc is not None:
relative_path = relative_doc.file_path().parent
diff --git a/specs/gen_docs/gen/doc_relationship_diagrams.py b/specs/gen_docs/gen/doc_relationship_diagrams.py
index b7a0d68b2b..7f3c74fc3e 100644
--- a/specs/gen_docs/gen/doc_relationship_diagrams.py
+++ b/specs/gen_docs/gen/doc_relationship_diagrams.py
@@ -51,7 +51,7 @@ def generate(self) -> bool: # noqa: C901
file_title = textwrap.fill(f"{file_id} Document Relationships", width=30)
dot_file = DotFile(
- self._document_name, file_title, depth=self._depth, title_size=150 if self._document_name is None else 50
+ file_id, file_title, depth=self._depth, title_size=150 if self._document_name is None else 50
)
all_dst_refs: list[str] = []
@@ -66,7 +66,7 @@ def generate(self) -> bool: # noqa: C901
doc_data = self._spec.get_document(doc)
# Add content type explicitely to table.
- doc_table.add_row(TableRow(name="content type", value=doc_data.headers["content type"].value))
+ doc_table.add_row(TableRow(name="content type", value=doc_data.content_type))
# Add all used Metadata to table.
for meta in self._spec.all_headers(HeaderType.METADATA):
diff --git a/specs/gen_docs/gen/docs_page_md.py b/specs/gen_docs/gen/docs_page_md.py
index 970f2d4836..b07c17f440 100644
--- a/specs/gen_docs/gen/docs_page_md.py
+++ b/specs/gen_docs/gen/docs_page_md.py
@@ -1,11 +1,8 @@
"""Generate the individual pages docs/.md file."""
import argparse
-import json
import typing
-from pydantic import HttpUrl
-
from gen.doc_generator import DocGenerator
from gen.doc_relationship_diagrams import DocRelationshipFile
from spec.signed_doc import SignedDoc
@@ -62,24 +59,7 @@ def document_payload(self) -> str:
if self._doc.payload is None:
return self.TODO_MSG
- payload_docs = self._doc.payload.description + "\n"
-
- schema = self._doc.payload.doc_schema
- if schema is not None:
- if isinstance(schema, HttpUrl):
- if schema == "https://json-schema.org/draft-07/schema":
- payload_docs += "\n**Must be a valid JSON Schema Draft 7 document.**"
- else:
- payload_docs += f"\nMust be a valid according to <{schema}>."
- else:
- payload_docs += f"""\nSchema :
-
-```json
-{json.dumps(schema, indent=2, sort_keys=True)}
-```
-
-"""
- return payload_docs.strip()
+ return f"{self._doc.payload}"
def document_signers(self) -> str:
"""Generate documentation about who may sign this documents."""
diff --git a/specs/gen_docs/gen/graphviz_doc_diagram.py b/specs/gen_docs/gen/graphviz_doc_diagram.py
index 3c2ec65b70..2c1d78ce06 100644
--- a/specs/gen_docs/gen/graphviz_doc_diagram.py
+++ b/specs/gen_docs/gen/graphviz_doc_diagram.py
@@ -71,6 +71,11 @@ class TableRow(BaseModel):
model_config = ConfigDict(extra="forbid")
+ @classmethod
+ def default_list(cls) -> list["TableRow"]:
+ """Return a default list of this class."""
+ return []
+
def generate(self, bgcolor: str) -> str:
"""Generate a single row of the table."""
value = self.value
@@ -171,6 +176,7 @@ def from_doc_cluster(cls, cluster: DocCluster | None) -> "Cluster | None":
return None
return cls(name=cluster.name)
+ @property
def label(self) -> str:
"""Transform the name into a label."""
return "cluster_" + self.name.lower().replace(" ", "_").replace("-", "_")
@@ -178,7 +184,7 @@ def label(self) -> str:
def start(self) -> str:
"""Start a new cluster."""
return f"""
-subgraph {self.label()} {{
+subgraph {self.label} {{
label = "{self.name}";
color=blue
penwidth=20
@@ -188,7 +194,7 @@ def end(self) -> str:
"""End the cluster."""
return "}\n"
- def __eq__(self, other: "Cluster") -> bool:
+ def __eq__(self, other: object) -> bool:
"""Eq."""
if not isinstance(other, Cluster):
# don't attempt to compare against unrelated types
@@ -203,7 +209,7 @@ class DotSignedDoc(BaseModel):
title_port: str = Field(default="title")
title_href: str | None = Field(default=None)
theme: TableTheme = Field(default_factory=TableTheme)
- rows: list[TableRow] = Field(default_factory=list)
+ rows: list[TableRow] = Field(default_factory=TableRow.default_list)
cluster: Cluster | None = Field(default=None)
model_config = ConfigDict(extra="forbid")
@@ -296,6 +302,11 @@ def is_cluster(self) -> bool:
"""Is the link to a cluster."""
return isinstance(self.port, Cluster)
+ @property
+ def port_label(self) -> str | None:
+ """Get label of the port."""
+ return self.port.label if self.is_cluster else None # type: ignore # noqa: PGH003
+
def __str__(self) -> str:
"""Str."""
name = f'"{self.id}"'
@@ -305,7 +316,7 @@ def __str__(self) -> str:
name += f":{self.dir}"
return name
- def __eq__(self, other: "DotLinkEnd") -> bool:
+ def __eq__(self, other: object) -> bool:
"""Eq."""
if not isinstance(other, DotLinkEnd):
# don't attempt to compare against unrelated types
@@ -335,12 +346,10 @@ def model_post_init(self, context: typing.Any) -> None: # noqa: ANN401
super().model_post_init(context)
# Add cluster parameters to the theme.
- if self.src.is_cluster:
- self.theme.ltail = self.src.port.label()
- if self.dst.is_cluster:
- self.theme.lhead = self.dst.port.label()
+ self.theme.ltail = self.src.port_label
+ self.theme.lhead = self.dst.port_label
- def __eq__(self, other: "DotLink") -> bool:
+ def __eq__(self, other: object) -> bool:
"""Eq."""
if not isinstance(other, DotLink):
# don't attempt to compare against unrelated types
@@ -367,20 +376,20 @@ def __init__(self, file_id: str, title: str, depth: int = 0, title_size: int = 1
self.title = title
self.title_size = title_size
self.rankdir = "LR"
- self.graph = {
+ self.graph: dict[str, str | int] = {
"fontname": DEFAULT_FONT_NAME,
"fontsize": DEFAULT_FONT_SIZE,
"fontcolor": DEFAULT_FONT_COLOR,
"bgcolor": "white",
}
- self.node = {
+ self.node: dict[str, str | int] = {
"penwidth": 0,
"margin": 0,
"fontname": DEFAULT_FONT_NAME,
"fontsize": DEFAULT_FONT_SIZE,
"fontcolor": DEFAULT_FONT_COLOR,
}
- self.edge = {
+ self.edge: dict[str, str | int] = {
"fontname": DEFAULT_FONT_NAME,
"fontsize": DEFAULT_FONT_SIZE,
"fontcolor": "red",
@@ -401,7 +410,7 @@ def add_table(self, table: DotSignedDoc) -> None:
cluster_name = None
if table.cluster is not None:
cluster_name = table.cluster.name
- if cluster_name is not None and cluster_name not in self.clusters:
+ if cluster_name is not None and cluster_name not in self.clusters and table.cluster is not None:
self.clusters[cluster_name] = table.cluster
if cluster_name not in self.tables:
self.tables[cluster_name] = {}
@@ -438,9 +447,9 @@ def clustered_tables(self) -> str:
def __str__(self) -> str:
"""Generate the DOT file."""
- def defaults(name: str, settings: dict) -> str:
+ def defaults(name: str, settings: dict[str, str | int]) -> str:
"""Expand the defaults."""
- defaults = []
+ defaults: list[str] = []
for default, value in settings.items():
defaults.append(f'{default}="{value}"')
return f"{name} [{', '.join(defaults)}];"
diff --git a/specs/gen_docs/gen/spec_md.py b/specs/gen_docs/gen/spec_md.py
index bd8638c8db..f70b4345ac 100644
--- a/specs/gen_docs/gen/spec_md.py
+++ b/specs/gen_docs/gen/spec_md.py
@@ -23,7 +23,7 @@ def header_parameter_doc(self, header: str, header_type: HeaderType) -> str:
if not isinstance(label, str):
custom_header = ""
header_format = options["format"]
- header_value = options.get("value", None)
+ header_value: str | list[str] | None = options.get("value", None)
header_format_display = f"{header_format}"
if isinstance(header_value, list) and len(header_value) > 0:
header_format_display += "\n * Supported Values:"
@@ -93,6 +93,40 @@ def generate(self) -> bool:
Specifically, the COSE Sign format is used.
This allows one or more signatures to be attached to the same document.
+While every Catalyst Signed Document is a valid COSE Sign format document,
+not every COSE Sign format document is a valid Catalyst Signed Document.
+The following restrictions apply:
+
+### Unprotected Headers are not permitted
+
+It is a requirement that any document that contains exactly the same data, must produce the same
+catalyst signed document.
+This means that unprotected headers, which do not form part of the data protected by
+the signature are not permitted.
+Any document which contains any unprotected headers is not a valid Catalyst Signed Document,
+even though it may be a valid COSE Sign formatted document.
+
+### Only defined metadata and COSE Headers are allowed
+
+Each document type, defines a set of metadata and the COSE Headers which are allowed in that document type.
+Even if the Catalyst Signed document metadata exists in this specification, IF it is not defined as
+a valid metadata or COSE Header field for that particular document it may not be present.
+Unexpected but otherwise valid Metadata or COSE Header fields invalidate the Catalyst Signed Document.
+
+### No undefined metadata or unused COSE Headers may be present
+
+COSE Header Fields which are defined by the COSE Specification, but are NOT defined as part of a
+Catalyst Signed Document may not be present.
+Any such COSE Header Fields present in the document render it an invalid Catalyst Signed Document.
+
+Any metadata field that is not defined in this specification may not be present in any protected header.
+Unrecognized metadata fields in a document render it an invalid Catalyst Signed Document.
+
+### CBOR Deterministic Encoding MUST be used
+
+The Catalyst Signed Document **MUST** be encoded using CBOR Deterministic Encoding.
+The "length-first core deterministic encoding requirements" variant of deterministic encoding *MUST* be used.
+
### Signed Document CDDL Definition
{signed_doc_cddl.markdown_reference(relative_doc=self)}
diff --git a/specs/gen_docs/pyproject.toml b/specs/gen_docs/pyproject.toml
index 6626530ffb..5ac71cbe38 100644
--- a/specs/gen_docs/pyproject.toml
+++ b/specs/gen_docs/pyproject.toml
@@ -5,6 +5,7 @@ description = "Generate Signed Document documentation files."
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
+ "jsonschema[format]>=4.23.0",
"pydantic>=2.11.4",
"pydot>=3.0.4",
"rich>=14.0.0",
diff --git a/specs/gen_docs/spec/document.py b/specs/gen_docs/spec/document.py
index 1f9e8a9d39..c9c5414347 100644
--- a/specs/gen_docs/spec/document.py
+++ b/specs/gen_docs/spec/document.py
@@ -20,6 +20,11 @@ class DocumentBusinessLogic(BaseModel):
model_config = ConfigDict(extra="forbid")
+def empty_string_list() -> list[str]:
+ """Get an empty string list."""
+ return []
+
+
class Document(BaseModel):
"""Document Data Definition."""
@@ -38,8 +43,8 @@ class Document(BaseModel):
versions: list[ChangeLogEntry]
_name: str | None = PrivateAttr(default=None)
- _all_refs: list[str] = PrivateAttr(default_factory=list)
- _refed_by: list[str] = PrivateAttr(default_factory=list)
+ _all_refs: list[str] = PrivateAttr(default_factory=empty_string_list)
+ _refed_by: list[str] = PrivateAttr(default_factory=empty_string_list)
doc_name: str | None = Field(default=None) # Set when wwe get a document
@@ -74,11 +79,21 @@ def all_references(self) -> list[str]:
return self._all_refs
@property
- def name(self) -> list[str]:
+ def name(self) -> str:
"""Get name of this document."""
- return self._name
+ return self._name if self._name is not None else "Unknown"
@property
def all_docs_referencing(self) -> list[str]:
"""Get name of all documents which reference this document."""
return self._refed_by
+
+ @property
+ def content_type(self) -> str | list[str]:
+ """Get document content type."""
+ content_type = self.headers.get("content type")
+ if content_type is not None:
+ content_type = content_type.value
+ if content_type is None:
+ content_type = "Undefined"
+ return content_type
diff --git a/specs/gen_docs/spec/payload.py b/specs/gen_docs/spec/payload.py
index 732682508c..3caa4efdc0 100644
--- a/specs/gen_docs/spec/payload.py
+++ b/specs/gen_docs/spec/payload.py
@@ -1,14 +1,122 @@
"""Payload Specification."""
+import json
+import textwrap
+import urllib
+import urllib.request
from typing import Any
+import jsonschema
+import rich
from pydantic import BaseModel, ConfigDict, Field, HttpUrl
+class PayloadExample(BaseModel):
+ """An Example of the payload."""
+
+ title: str
+ description: str
+ example: dict[str, Any]
+
+ model_config = ConfigDict(extra="forbid")
+
+ @classmethod
+ def default(cls) -> list["PayloadExample"]:
+ """Return Default list."""
+ return []
+
+ def __str__(self) -> str:
+ """Get the example properly formatted as markdown."""
+ example = json.dumps(self.example, indent=2, sort_keys=True)
+ textwrap.indent(example, " ")
+
+ return f"""
+
+
+??? example "Example: {self.title}"
+
+{textwrap.indent(self.description, " ")}
+
+ ```json
+{textwrap.indent(example, " ")}
+ ```
+
+
+""".strip()
+
+
+class SchemaValidationError(Exception):
+ """Something is wrong with payload schema validation."""
+
+
class Payload(BaseModel):
"""Payload Deserialized Specification."""
description: str
doc_schema: HttpUrl | dict[str, Any] | None = Field(default=None, alias="schema")
+ examples: list[PayloadExample] = Field(default_factory=PayloadExample.default)
model_config = ConfigDict(extra="forbid")
+
+ def model_post_init(self, context: Any) -> None: # noqa: ANN401
+ """Validate the examples against the schema."""
+ schema = None
+ validator = None
+ if isinstance(self.doc_schema, HttpUrl):
+ if f"{self.doc_schema}" == "https://json-schema.org/draft-07/schema":
+ schema = jsonschema.Draft7Validator.META_SCHEMA
+ else:
+ rich.print(f"Downloading Schema from: {self.doc_schema}")
+ with urllib.request.urlopen(f"{self.doc_schema}") as response: # noqa: S310
+ schema = json.loads(response.read())
+ elif isinstance(self.doc_schema, dict):
+ schema = self.doc_schema
+
+ if schema is not None:
+ # Check that its valid jsonschema Draft 7.
+ jsonschema.Draft7Validator.check_schema(schema)
+ validator = jsonschema.Draft7Validator(schema, format_checker=jsonschema.draft7_format_checker)
+
+ for example in self.examples:
+ if validator is None:
+ msg = "No schema to validate payload examples."
+ raise SchemaValidationError(msg)
+ validator.validate(instance=example.example) # type: ignore # noqa: PGH003
+
+ return super().model_post_init(context)
+
+ def __str__(self) -> str:
+ """Get the examples properly formatted as markdown."""
+ docs = self.description + "\n"
+
+ schema = self.doc_schema
+ if schema is not None:
+ if isinstance(schema, HttpUrl):
+ if schema == "https://json-schema.org/draft-07/schema":
+ docs += "\n**Must be a valid JSON Schema Draft 7 document.**"
+ else:
+ docs += f"\nMust be a valid according to <{schema}>."
+ else:
+ docs += f"""\n### Schema
+
+
+??? abstract
+
+{textwrap.indent(self.description, " ")}
+
+ ```json
+{textwrap.indent(json.dumps(schema, indent=2, sort_keys=True), " ")}
+ ```
+
+
+"""
+
+ if len(self.examples) > 0:
+ docs += "\n### Example"
+ if len(self.examples) >= 2: # noqa: PLR2004
+ docs += "s"
+ docs += "\n"
+ for example in self.examples:
+ docs += f"{example}\n"
+
+ return docs.strip()
diff --git a/specs/gen_docs/spec/signed_doc.py b/specs/gen_docs/spec/signed_doc.py
index e9f832b361..ad1ae1d771 100644
--- a/specs/gen_docs/spec/signed_doc.py
+++ b/specs/gen_docs/spec/signed_doc.py
@@ -32,17 +32,17 @@ class HeaderType(Enum):
HEADERS: dict[str, dict[str, str]] = {
- HeaderType.DOCUMENT: {
+ HeaderType.DOCUMENT.name: {
"headers": "cose_headers",
"order": "cose_headers_order",
"format": "coseHeaderFormats",
},
- HeaderType.SIGNATURE: {
+ HeaderType.SIGNATURE.name: {
"headers": "cose_signature_headers",
"order": "cose_signature_headers_order",
"format": "coseHeaderFormats",
},
- HeaderType.METADATA: {
+ HeaderType.METADATA.name: {
"headers": "metadata",
"order": "metadata_order",
"format": "metadataFormats",
@@ -80,7 +80,7 @@ class SignedDoc(BaseModel):
def load(cls, spec_file: str) -> typing.Self:
"""Initialize the Signed Document Specification."""
with Path(spec_file).open("r") as f:
- data: dict = json.load(f)
+ data: dict[str, typing.Any] = json.load(f)
doc = cls(**data)
doc._data = data
doc._file = spec_file
@@ -121,19 +121,19 @@ def doc_in_cluster_name(self, doc_name: str) -> str | None:
return cluster.name
return None
- def data(self) -> dict:
+ def data(self) -> dict[str, typing.Any]:
"""Return the raw spec data."""
return self._data
def document_names(self) -> list[str]:
"""Get all documents."""
- return self.docs.keys()
+ return list(self.docs.keys())
def format_names(self, header_type: HeaderType) -> list[str]:
"""Get a list of all metadata format names defined."""
_, _, formats = self.headers_and_order(header_type=header_type)
- metadata_formats: dict = self._data[formats]
- return metadata_formats.keys()
+ metadata_formats: dict[str, typing.Any] = self._data[formats]
+ return list(metadata_formats.keys())
def link_name_aka(self, link_name: str) -> str | None:
"""Get a Link AKA for a link name, or None if it doesn't exist."""
@@ -151,9 +151,9 @@ def link_names(self) -> list[str]:
def link_for_link_name(self, link_name: str) -> str:
"""Get a link for a link name."""
- return self.documentation_links[link_name]
+ return f"{self.documentation_links[link_name]}"
- def header(self, header: str, header_type: HeaderType = HeaderType.DOCUMENT) -> dict:
+ def header(self, header: str, header_type: HeaderType = HeaderType.DOCUMENT) -> dict[str, typing.Any]:
"""Get Cose header definition."""
headers, _, _ = self.headers_and_order(header_type)
return headers[header]
@@ -166,14 +166,14 @@ def encoding_type_description(self, encoding_type: str) -> str:
"""Get a description for a known content type."""
return self.encoding_types[encoding_type].description
- def headers_and_order(self, header_type: HeaderType) -> tuple[dict, list[str], str]:
+ def headers_and_order(self, header_type: HeaderType) -> tuple[dict[str, typing.Any], list[str], str]:
"""Get headers and their ordering for a header_type."""
- headers = HEADERS[header_type]["headers"]
- header_order = HEADERS[header_type]["order"]
- formats = HEADERS[header_type]["format"]
+ headers_key = HEADERS[header_type.name]["headers"]
+ header_order_key = HEADERS[header_type.name]["order"]
+ formats = HEADERS[header_type.name]["format"]
- headers: dict = self._data[headers]
- header_order: list[str] = self._data.get(header_order, [])
+ headers: dict[str, typing.Any] = self._data[headers_key]
+ header_order: list[str] = self._data.get(header_order_key, [])
# Make sure unordered headers get included in the documentation.
for header in headers:
@@ -187,7 +187,7 @@ def all_headers(self, header_type: HeaderType = HeaderType.DOCUMENT) -> list[str
_, header_order, _ = self.headers_and_order(header_type)
return header_order
- def cddl_type_for_metadata(self, name: str | None, header_type: str) -> str:
+ def cddl_type_for_metadata(self, name: str, header_type: HeaderType) -> str:
"""Get the CDDL type for a given Metadata field."""
headers, _, formats = self.headers_and_order(header_type)
@@ -198,19 +198,23 @@ def cddl_type_for_metadata(self, name: str | None, header_type: str) -> str:
cddl_def = self._data[formats].get(cddl_def)
if cddl_def is not None:
cddl_def = cddl_def.get("cddl")
+ if cddl_def is None:
+ cddl_def = "Unknown"
return cddl_def
- def cddl_def(self, name: str) -> dict | None: # noqa: C901
+ def cddl_def(self, name: str) -> dict[str, typing.Any] | None: # noqa: C901
"""Get a cddl definition by name."""
- def synthetic_headers(defs: dict, header_type: HeaderType = HeaderType.METADATA) -> dict:
+ def synthetic_headers( # noqa: C901
+ defs: dict[str, typing.Any], header_type: HeaderType = HeaderType.METADATA
+ ) -> dict[str, str]:
"""Generate a synthetic cddl def for this type.
Needs to be generated from Metadata definitions.
"""
cddl_def = ""
defs["requires"] = []
- exclusives = []
+ exclusives: list[str] = []
headers, header_names, _ = self.headers_and_order(header_type)
@@ -222,19 +226,22 @@ def synthetic_headers(defs: dict, header_type: HeaderType = HeaderType.METADATA)
if exclusive is not None:
exclusive.append(header)
exclusive.sort()
- if exclusive not in exclusives:
- exclusives.append(exclusive)
+ for excl in exclusive:
+ if excl not in exclusives:
+ exclusives.append(excl)
else:
+ requires: list[str] = defs["requires"]
cddl_type = self.cddl_type_for_metadata(header, header_type)
field_name = header_data.get("coseLabel", header)
if isinstance(field_name, str):
field_name = f'"{field_name}"'
cddl_def += f"{optional}{field_name} => {cddl_type}\n"
- if cddl_type not in defs["requires"]:
- defs["requires"].append(cddl_type)
+ if cddl_type not in requires:
+ requires.append(cddl_type)
+ defs["requires"] = requires
for exclusive_set in exclusives:
# Exclusive sets are never required
- exclusive_fields = []
+ exclusive_fields: list[str] = []
for entry in exclusive_set:
cddl_type = self.cddl_type_for_metadata(entry, header_type)
field_name = headers[entry].get("coseLabel", entry)
diff --git a/specs/gen_docs/uv.lock b/specs/gen_docs/uv.lock
index 5a05c6644e..06aaec055a 100644
--- a/specs/gen_docs/uv.lock
+++ b/specs/gen_docs/uv.lock
@@ -6,9 +6,40 @@ requires-python = ">=3.13"
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload_time = "2024-05-20T21:33:25.928Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload_time = "2024-05-20T21:33:24.1Z" },
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
+]
+
+[[package]]
+name = "arrow"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "python-dateutil" },
+ { name = "types-python-dateutil" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size = 131960, upload-time = "2023-09-30T22:11:18.25Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419, upload-time = "2023-09-30T22:11:16.072Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "25.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
+]
+
+[[package]]
+name = "fqdn"
+version = "1.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" },
]
[[package]]
@@ -16,6 +47,7 @@ name = "gen-docs"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
+ { name = "jsonschema", extra = ["format"] },
{ name = "pydantic" },
{ name = "pydot" },
{ name = "rich" },
@@ -24,12 +56,82 @@ dependencies = [
[package.metadata]
requires-dist = [
+ { name = "jsonschema", extras = ["format"], specifier = ">=4.23.0" },
{ name = "pydantic", specifier = ">=2.11.4" },
{ name = "pydot", specifier = ">=3.0.4" },
{ name = "rich", specifier = ">=14.0.0" },
{ name = "rich-argparse", specifier = ">=1.7.0" },
]
+[[package]]
+name = "idna"
+version = "3.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
+]
+
+[[package]]
+name = "isoduration"
+version = "20.11.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "arrow" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" },
+]
+
+[[package]]
+name = "jsonpointer"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" },
+]
+
+[[package]]
+name = "jsonschema"
+version = "4.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "jsonschema-specifications" },
+ { name = "referencing" },
+ { name = "rpds-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778, upload-time = "2024-07-08T18:40:05.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462, upload-time = "2024-07-08T18:40:00.165Z" },
+]
+
+[package.optional-dependencies]
+format = [
+ { name = "fqdn" },
+ { name = "idna" },
+ { name = "isoduration" },
+ { name = "jsonpointer" },
+ { name = "rfc3339-validator" },
+ { name = "rfc3987" },
+ { name = "uri-template" },
+ { name = "webcolors" },
+]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2025.4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "referencing" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" },
+]
+
[[package]]
name = "markdown-it-py"
version = "3.0.0"
@@ -37,18 +139,18 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload_time = "2023-06-03T06:41:14.443Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload_time = "2023-06-03T06:41:11.019Z" },
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload_time = "2022-08-14T12:40:10.846Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload_time = "2022-08-14T12:40:09.779Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
@@ -61,9 +163,9 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload_time = "2025-04-29T20:38:55.02Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload-time = "2025-04-29T20:38:55.02Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload_time = "2025-04-29T20:38:52.724Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" },
]
[[package]]
@@ -73,25 +175,25 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload_time = "2025-04-23T18:33:52.104Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload_time = "2025-04-23T18:31:53.175Z" },
- { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload_time = "2025-04-23T18:31:54.79Z" },
- { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload_time = "2025-04-23T18:31:57.393Z" },
- { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload_time = "2025-04-23T18:31:59.065Z" },
- { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload_time = "2025-04-23T18:32:00.78Z" },
- { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload_time = "2025-04-23T18:32:02.418Z" },
- { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload_time = "2025-04-23T18:32:04.152Z" },
- { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload_time = "2025-04-23T18:32:06.129Z" },
- { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload_time = "2025-04-23T18:32:08.178Z" },
- { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload_time = "2025-04-23T18:32:10.242Z" },
- { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload_time = "2025-04-23T18:32:12.382Z" },
- { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload_time = "2025-04-23T18:32:14.034Z" },
- { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload_time = "2025-04-23T18:32:15.783Z" },
- { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload_time = "2025-04-23T18:32:18.473Z" },
- { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload_time = "2025-04-23T18:32:20.188Z" },
- { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload_time = "2025-04-23T18:32:22.354Z" },
- { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload_time = "2025-04-23T18:32:25.088Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" },
+ { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" },
+ { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" },
+ { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" },
+ { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" },
+ { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
]
[[package]]
@@ -101,27 +203,73 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyparsing" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167, upload_time = "2025-01-05T16:18:45.763Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167, upload-time = "2025-01-05T16:18:45.763Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776, upload_time = "2025-01-05T16:18:42.836Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776, upload-time = "2025-01-05T16:18:42.836Z" },
]
[[package]]
name = "pygments"
version = "2.19.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload_time = "2025-01-06T17:26:30.443Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload_time = "2025-01-06T17:26:25.553Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
]
[[package]]
name = "pyparsing"
version = "3.2.3"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload_time = "2025-03-25T05:01:28.114Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload_time = "2025-03-25T05:01:24.908Z" },
+ { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" },
+]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
+]
+
+[[package]]
+name = "referencing"
+version = "0.36.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "rpds-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
+]
+
+[[package]]
+name = "rfc3339-validator"
+version = "0.1.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" },
+]
+
+[[package]]
+name = "rfc3987"
+version = "1.3.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/14/bb/f1395c4b62f251a1cb503ff884500ebd248eed593f41b469f89caa3547bd/rfc3987-1.3.8.tar.gz", hash = "sha256:d3c4d257a560d544e9826b38bc81db676890c79ab9d7ac92b39c7a253d5ca733", size = 20700, upload-time = "2018-07-29T17:23:47.954Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/65/d4/f7407c3d15d5ac779c3dd34fbbc6ea2090f77bd7dd12f207ccf881551208/rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53", size = 13377, upload-time = "2018-07-29T17:23:45.313Z" },
]
[[package]]
@@ -132,9 +280,9 @@ dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload_time = "2025-03-30T14:15:14.23Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload_time = "2025-03-30T14:15:12.283Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" },
]
[[package]]
@@ -144,18 +292,70 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "rich" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/aa/b9/ff53663ee7fa6a4195fa96d91da499f2e00ca067541e016d345cce1c9ad2/rich_argparse-1.7.0.tar.gz", hash = "sha256:f31d809c465ee43f367d599ccaf88b73bc2c4d75d74ed43f2d538838c53544ba", size = 38009, upload_time = "2025-02-08T19:00:20.755Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/b9/ff53663ee7fa6a4195fa96d91da499f2e00ca067541e016d345cce1c9ad2/rich_argparse-1.7.0.tar.gz", hash = "sha256:f31d809c465ee43f367d599ccaf88b73bc2c4d75d74ed43f2d538838c53544ba", size = 38009, upload-time = "2025-02-08T19:00:20.755Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/bb/9c/dc7cbeb99a7b7422392ed7f327efdbb958bc0faf424aef5f130309320bda/rich_argparse-1.7.0-py3-none-any.whl", hash = "sha256:b8ec8943588e9731967f4f97b735b03dc127c416f480a083060433a97baf2fd3", size = 25339, upload_time = "2025-02-08T19:00:17.911Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/9c/dc7cbeb99a7b7422392ed7f327efdbb958bc0faf424aef5f130309320bda/rich_argparse-1.7.0-py3-none-any.whl", hash = "sha256:b8ec8943588e9731967f4f97b735b03dc127c416f480a083060433a97baf2fd3", size = 25339, upload-time = "2025-02-08T19:00:17.911Z" },
+]
+
+[[package]]
+name = "rpds-py"
+version = "0.25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/d2/7bed8453e53f6c9dea7ff4c19ee980fd87be607b2caf023d62c6579e6c30/rpds_py-0.25.0.tar.gz", hash = "sha256:4d97661bf5848dd9e5eb7ded480deccf9d32ce2cd500b88a26acbf7bd2864985", size = 26822, upload-time = "2025-05-15T13:42:03.815Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/d9/6534d5a9d00038261894551ee8043267f17c019e6c0df3c7d822fb5914f1/rpds_py-0.25.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4e5fe366fa53bd6777cf5440245366705338587b2cf8d61348ddaad744eb591a", size = 364375, upload-time = "2025-05-15T13:39:25.878Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9d/f90c079635017cc50350cbbbf2c4fea7b2a75a24bea92211da1b0c52d55f/rpds_py-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:54f925ff8d4443b7cae23a5215954abbf4736a3404188bde53c4d744ac001d89", size = 350284, upload-time = "2025-05-15T13:39:27.336Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/04/b54c5b3abdccf03ca3ec3317bd68caaa7907a61fea063096ee08d128b6ed/rpds_py-0.25.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58258a66255b2500ddaa4f33191ada5ec983a429c09eb151daf81efbb9aa115", size = 392107, upload-time = "2025-05-15T13:39:30.99Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/99/001bc3ab81c1798ee4c7bba7950134258d899e566d6839b6696b47248f71/rpds_py-0.25.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f3a57f08c558d0983a708bfe6d1265f47b5debff9b366b2f2091690fada055c", size = 398612, upload-time = "2025-05-15T13:39:32.505Z" },
+ { url = "https://files.pythonhosted.org/packages/00/e1/e22893e1043938811a50c857a5780e0a4e2da02dd10ac041ecca1044906a/rpds_py-0.25.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7d60d42f1b9571341ad2322e748f7a60f9847546cd801a3a0eb72a1b54c6519", size = 452190, upload-time = "2025-05-15T13:39:34.024Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/6c/7071e6d27e784ac33ab4ca048eb550b5fc4f381b29e9ba33254bc6e7eaf6/rpds_py-0.25.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a54b94b0e4de95aa92618906fb631779d9fde29b4bf659f482c354a3a79fd025", size = 440634, upload-time = "2025-05-15T13:39:36.048Z" },
+ { url = "https://files.pythonhosted.org/packages/57/17/7343ea3ec906ee8c2b64a956d702de5067e0058b5d2869fbfb4b11625112/rpds_py-0.25.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af1c2241919304cc2f90e7dcb3eb1c1df6fb4172dd338e629dd6410e48b3d1a0", size = 391000, upload-time = "2025-05-15T13:39:37.802Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/ad/9b3c3e950108073448834f0548077e598588efa413ba8dcc91e7ad6ff59d/rpds_py-0.25.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7d34547810bfd61acf8a441e8a3651e7a919e8e8aed29850be14a1b05cfc6f41", size = 424621, upload-time = "2025-05-15T13:39:39.409Z" },
+ { url = "https://files.pythonhosted.org/packages/57/06/bd99ca30a6e539c18c6175501c1dd7f9ef0640f7b1dc0b14b094785b509a/rpds_py-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66568caacf18542f0cf213db7adf3de2da6ad58c7bf2c4fafec0d81ae557443b", size = 569529, upload-time = "2025-05-15T13:39:41.011Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/79/93381a25668466502adc082d3ce2a9ff35f8116e5e2711cedda0bfcfd699/rpds_py-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e49e4c3e899c32884d7828c91d6c3aff08d2f18857f50f86cc91187c31a4ca58", size = 594638, upload-time = "2025-05-15T13:39:43.15Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ee/371ecc045d65af518e2210ad018892b1f7a7a21cd64661156b4d29dfd839/rpds_py-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:20af08b0b2d5b196a2bcb70becf0b97ec5af579cee0ae6750b08a2eea3b6c77d", size = 561413, upload-time = "2025-05-15T13:39:45.3Z" },
+ { url = "https://files.pythonhosted.org/packages/34/c4/85e9853312b7e5de3c98f100280fbfd903e63936f49f6f11e4cd4eb53299/rpds_py-0.25.0-cp313-cp313-win32.whl", hash = "sha256:d3dc8d6ce8f001c80919bdb49d8b0b815185933a0b8e9cdeaea42b0b6f27eeb0", size = 222326, upload-time = "2025-05-15T13:39:46.777Z" },
+ { url = "https://files.pythonhosted.org/packages/65/c6/ac744cc5752b6f291b2cf13e19cd7ea3cafe68922239a3b95f05f39287b7/rpds_py-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:113d134dc5a8d2503630ca2707b58a1bf5b1b3c69b35c7dab8690ee650c111b8", size = 234772, upload-time = "2025-05-15T13:39:48.804Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/aa/dabab50a2fb321a12ffe4668087e5d0f9b06286ccb260d345bf01c79b07c/rpds_py-0.25.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:6c72a4a8fab10bc96720ad40941bb471e3b1150fb8d62dab205d495511206cf1", size = 359693, upload-time = "2025-05-15T13:39:53.913Z" },
+ { url = "https://files.pythonhosted.org/packages/11/3d/acda0095fe54ee6c553d222fb3d275506f8db4198b6a72a69eef826d63c1/rpds_py-0.25.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bb979162323f3534dce84b59f86e689a0761a2a300e0212bfaedfa80d4eb8100", size = 345911, upload-time = "2025-05-15T13:39:55.623Z" },
+ { url = "https://files.pythonhosted.org/packages/db/f3/fba9b387077f9b305fce27fe22bdb731b75bfe208ae005fd09a127eced05/rpds_py-0.25.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35c8cb5dcf7d36d3adf2ae0730b60fb550a8feb6e432bee7ef84162a0d15714b", size = 387669, upload-time = "2025-05-15T13:39:57.103Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/a7/b8dbcdc9a8f1e96b5abc58bdfc22f2845178279694a9294fe4feb66ae330/rpds_py-0.25.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:673ba018df5ae5e7b6c9a021d51ffe39c0ae1daa0041611ed27a0bca634b2d2e", size = 392202, upload-time = "2025-05-15T13:39:59.456Z" },
+ { url = "https://files.pythonhosted.org/packages/60/60/2d46ad24207114cdb341490387d5a77c845827ac03f2a37182a19d072738/rpds_py-0.25.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16fb28d3a653f67c871a47c5ca0be17bce9fab8adb8bcf7bd09f3771b8c4d860", size = 450080, upload-time = "2025-05-15T13:40:01.131Z" },
+ { url = "https://files.pythonhosted.org/packages/85/ae/b1966ca161942f2edf0b2c4fb448b88c19bdb37e982d0907c4b484eb0bbc/rpds_py-0.25.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12a84c3851f9e68633d883c01347db3cb87e6160120a489f9c47162cd276b0a5", size = 438189, upload-time = "2025-05-15T13:40:02.816Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/b0/0a8bff40865e27fc8cd7bdf667958981794ccf5e7989890ae96c89112920/rpds_py-0.25.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b5f457afffb45d3804728a54083e31fbaf460e902e3f7d063e56d0d0814301e", size = 387925, upload-time = "2025-05-15T13:40:04.523Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/5d/62abbc77e18f9e67556ead54c84a7c662f39236b7a41cf1a39a24bf5e79f/rpds_py-0.25.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9442cbff21122e9a529b942811007d65eabe4182e7342d102caf119b229322c6", size = 417682, upload-time = "2025-05-15T13:40:06.879Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/eb/2f65e4332e3566d06c5ccad64441b1eaaf58a6c5999d533720f1f47d3118/rpds_py-0.25.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:383cf0d4288baf5a16812ed70d54ecb7f2064e255eb7fe42c38e926adeae4534", size = 565244, upload-time = "2025-05-15T13:40:08.598Z" },
+ { url = "https://files.pythonhosted.org/packages/02/3a/ae5f68ab4879d6fbe3abec3139eab1664c3372d8b42864ab940a4940a61c/rpds_py-0.25.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0dcdee07ebf76223092666c72a9552db276fbe46b98830ecd1bb836cc98adc81", size = 590459, upload-time = "2025-05-15T13:40:10.375Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/f6/ada6c3d9b803a9eb7bc9c8b3f3cebf7d779bbbb056cd7e3fc150e4c74c00/rpds_py-0.25.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5bbfbd9c74c4dd74815bd532bf29bedea6d27d38f35ef46f9754172a14e4c655", size = 558335, upload-time = "2025-05-15T13:40:13.695Z" },
+ { url = "https://files.pythonhosted.org/packages/68/9a/7d269e8f1bfe3143e699334ca0b578e16b37e6505bf10dca8c02aa8addc8/rpds_py-0.25.0-cp313-cp313t-win32.whl", hash = "sha256:90dbd2c42cb6463c07020695800ae8f347e7dbeff09da2975a988e467b624539", size = 218761, upload-time = "2025-05-15T13:40:16.043Z" },
+ { url = "https://files.pythonhosted.org/packages/16/16/f5843b19b7bfd16d63b960cf4c646953010886cc62dd41b00854d77b0eed/rpds_py-0.25.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8c2ad59c4342a176cb3e0d5753e1c911eabc95c210fc6d0e913c32bf560bf012", size = 232634, upload-time = "2025-05-15T13:40:17.633Z" },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
+]
+
+[[package]]
+name = "types-python-dateutil"
+version = "2.9.0.20250516"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/88/d65ed807393285204ab6e2801e5d11fbbea811adcaa979a2ed3b67a5ef41/types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5", size = 13943, upload-time = "2025-05-16T03:06:58.385Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/3f/b0e8db149896005adc938a1e7f371d6d7e9eca4053a29b108978ed15e0c2/types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93", size = 14356, upload-time = "2025-05-16T03:06:57.249Z" },
]
[[package]]
name = "typing-extensions"
version = "4.13.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload_time = "2025-04-10T14:19:05.416Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload_time = "2025-04-10T14:19:03.967Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" },
]
[[package]]
@@ -165,7 +365,25 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload_time = "2025-02-25T17:27:59.638Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" },
+]
+
+[[package]]
+name = "uri-template"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" },
+]
+
+[[package]]
+name = "webcolors"
+version = "24.11.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size = 45064, upload-time = "2024-11-11T07:43:24.224Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload_time = "2025-02-25T17:27:57.754Z" },
+ { url = "https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size = 14934, upload-time = "2024-11-11T07:43:22.529Z" },
]
diff --git a/specs/signed_doc.json b/specs/signed_doc.json
index 9706ba0d6b..8c59764853 100644
--- a/specs/signed_doc.json
+++ b/specs/signed_doc.json
@@ -11,9 +11,13 @@
"Comment": "b679ded3-0e7c-41ba-89f8-da62a17898ea",
"Decision": "788ff4c6-d65a-451f-bb33-575fe056b411",
"ModerationAction": "a5d232b8-5e03-4117-9afd-be32b878fcdd",
+ "Profile": "1b70f611-518d-479e-be73-11b5e9cb68a5",
"Proposal": "7808d2ba-d511-40af-84e8-c0d1625fdfdc",
+ "RepresentativeCategoryProfile": "f1a2b3c4-1111-4abc-8def-2345678901aa",
+ "RepresentativeProfile": "e3f2c1b4-7890-4abc-8def-2345678901ef",
"SubmissionAction": "78927329-cfd9-4ea1-9c71-0e019b126a65",
- "Template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f"
+ "Template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "VoterRepresentativeDelegation": "f1a2b3c4-3333-4abc-8def-2345678901cc"
},
"cddlDefinitions": {
"COSE_Document_Header_Map": {
@@ -253,6 +257,11 @@
"changes": "* Use generalized parameters.",
"modified": "2025-05-05",
"version": "0.03"
+ },
+ {
+ "changes": "* Improve and make document serialization more repeatable, and stricter.\n* TODO: Define Systems parameters\n* TODO: Define DReps documents.\n* TODO: Define Proposer Profiles.\n* TODO: Define Role 0 Profile.",
+ "modified": "2025-05-30",
+ "version": "0.04"
}
]
},
@@ -925,15 +934,15 @@
}
]
},
- "Proposal": {
+ "Profile": {
"authors": {
- "Steven Johnson": "steven.johnson@iohk.io"
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
},
"business_logic": {
- "back_end": "Before accepting a new proposal to be published, the backend will ensure:\n\n* The document has been signed by a valid author or collaborator.\n* That the signer of the document was a registered proposer\n* That the document was signed with their proposers key\n* That all listed `collaborators` are registered as proposers.\n* That the document has been signed validly according to the [validation](#validation) rules.",
- "front_end": "As validity of the documents is currently enforced by the backend, \nthe front end does not need to validate the document has been signed\ncorrectly.\nIt may do so, but it is not required."
+ "back_end": "* Validate and store profile data against the referenced 'Profile_Template'.\n* This profile serves as the base document for a user.\n Its scope can be extended to create more specific profiles.",
+ "front_end": "* Display the user's profile information.\n* Allow a user to edit their own profile data."
},
- "description": "A Proposal is a document which describes a proposed solution or project to\naddress the criteria of a category within a campaign.\n\nThe proposal itself is a draft document, it is not submitted for consideration\nunless a `Proposal Submission Action` is submitted which references it.\n\nProposals themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal is controlled by its template.",
+ "description": "## Profile Document\n\nA minimal user profile that provides basic information about a user.\nIts structure is defined by the referenced Profile Template.\nIt is used as a base for more specific profiles like the Representative Profile.",
"headers": {
"content type": {
"coseLabel": 3,
@@ -957,7 +966,7 @@
"description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
"exclusive": null,
"format": "Collaborators Reference List",
- "required": "optional",
+ "required": "excluded",
"validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
},
"id": {
@@ -971,16 +980,7 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": [
- "template"
- ],
- "multiple": false,
- "required": "yes",
- "type": [
- "Brand Parameters",
- "Campaign Parameters",
- "Category Parameters"
- ],
+ "required": "excluded",
"validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
},
"ref": {
@@ -1001,7 +1001,7 @@
"description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
"exclusive": null,
"format": "Version Revocations",
- "required": "optional",
+ "required": "excluded",
"validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
},
"section": {
@@ -1018,7 +1018,7 @@
"linked_refs": null,
"multiple": false,
"required": "yes",
- "type": "Proposal Template",
+ "type": "Profile Template",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
"type": {
@@ -1038,43 +1038,39 @@
},
"notes": [],
"payload": {
- "description": "Proposal Document drafted for submission to a category of a campaign.\n\nMust be valid according to the schema contained within the \n`Document Reference` from the `template` metadata."
+ "description": "The profile payload contains all base profile fields.\nIts structure is defined by the referenced Profile Template."
},
"signers": {
"roles": {
"user": [
- "Proposer"
+ "Registered"
]
},
"update": {
- "author": true,
- "collaborators": true
+ "author": true
}
},
"type": [
- "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ "1b70f611-518d-479e-be73-11b5e9cb68a5"
],
- "validation": "The first version of a Proposal *MUST* be signed by the original author.\nIt may optionally be co-signed by any of the listed `collaborators`.\nIt may not be signed by anyone else.\n\nSubsequent Versions can be signed/co-signed by either the Original Author of the first version,\nOR any of the listed `collaborators` in the immediately previous version.\nThis allows any collaborator to update the next version of a document, provided they are still a collaborator.\nIt is valid for a proposal to be signed by a collaborator \nwho is no longer listed as in the `collaborators`\nof the document they are signing, provided they are listed as a collaborator in the immediately previous document version.\nThis allows for a collaborator to make an update to the document which removes themselves\nfrom the `collaborators` list.\n\nAll versions of the document *MUST* list the author as the original author.\nThe Author can not be changed by any document revision.",
+ "validation": "* The signer must be a registered 'User'.\n* The payload must be valid against the JSON schema defined in the referenced 'Profile Template'.",
"versions": [
{
"changes": "* First Published Version",
- "modified": "2025-04-04",
+ "modified": "2025-06-19",
"version": "0.01"
- },
- {
- "changes": "* Use generalized parameters.",
- "modified": "2025-05-05",
- "version": "0.03"
}
]
},
- "Proposal Comment": {
- "authors": {},
+ "Profile Template": {
+ "authors": {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ },
"business_logic": {
- "back_end": "The backend will only validate the document being referenced exists, \nand the integrity of the `ref` and `reply` metadata fields is correct.",
- "front_end": "Comments are valid for any version of the document, however\nas comments refer to a specific version of a document, they may\nlose context when displayed against the latest version of a document.\nIn these cases, the front end should clearly show that a comment was on\na different version of the document.\n\nIf the front end posts a reply to another comment: \n\n* it should reference the comment being replied to in the `reply` field. \n* The `ref` field must refer to the same document, but can be a different version."
+ "back_end": "* Validate and store the JSON schema that defines the structure for all 'Profile' documents.",
+ "front_end": ""
},
- "description": "A Proposal Comment is a document which comments on a referenced Proposal document.\n\nProposal Comments themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal comment is controlled by its template.",
+ "description": "## Profile Template Document\n\nDefines the allowed payload contents and constraints for a generic user profile.",
"headers": {
"content type": {
"coseLabel": 3,
@@ -1112,61 +1108,42 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": [
- "ref",
- "template"
- ],
- "multiple": false,
- "required": "yes",
- "type": [
- "Brand Parameters",
- "Campaign Parameters",
- "Category Parameters"
- ],
+ "required": "excluded",
"validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
},
"ref": {
"description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
- "multiple": false,
- "required": "yes",
- "type": "Proposal",
+ "required": "excluded",
"validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
},
"reply": {
"description": "Reference to a Comment document type being referred to.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
- "multiple": false,
- "required": "optional",
- "type": "Proposal Comment",
+ "required": "excluded",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
},
"revocations": {
"description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
"exclusive": null,
"format": "Version Revocations",
- "required": "optional",
+ "required": "excluded",
"validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
},
"section": {
"description": "A Reference to the original document, or the comment being replied to.",
"exclusive": null,
"format": "Section Reference",
- "required": "optional",
+ "required": "excluded",
"validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
},
"template": {
"description": "Reference to the template used to create and/or validate this document.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
- "multiple": false,
- "required": "yes",
- "type": "Proposal Comment Template",
+ "required": "excluded",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
"type": {
@@ -1186,10 +1163,44 @@
},
"notes": [],
"payload": {
- "description": "JSON Document which must validate against the referenced template."
+ "description": "JSON Schema document which defines the valid contents of a profile document.",
+ "schema": {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/profile_template.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "description": "Schema for a profile document template for any Catalyst actor.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "bio": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "bio"
+ ],
+ "title": "Profile Template Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
},
"signers": {
"roles": {
+ "admin": [
+ "Brand Admin"
+ ],
"user": [
"Registered"
]
@@ -1199,33 +1210,34 @@
}
},
"type": [
- "b679ded3-0e7c-41ba-89f8-da62a17898ea",
- "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "1b70f611-518d-479e-be73-11b5e9cb68a5"
],
- "validation": "A comment which is a reply *MUST* reference the same document.\nIt may reference a different version of the document.",
+ "validation": "* The signer MUST be a registered 'Admin'.\n* The payload MUST be a valid JSON schema.\n* The schema SHOULD define a minimal set of profile fields (e.g., name, bio).",
"versions": [
{
- "changes": "* First Published Version",
- "modified": "2025-04-04",
+ "changes": "\t* First Published Version",
+ "modified": "2025-06-19",
"version": "0.01"
- },
- {
- "changes": "* Use generalized parameters.",
- "modified": "2025-05-05",
- "version": "0.03"
}
]
},
- "Proposal Comment Meta Template": {
- "authors": {},
- "description": "## Proposal Comment Meta Template Document\n\nA Proposal Comment Meta Template is used to enforce functional requirements\nare met in any Proposal Comment Template.\n\nThe payload of a proposal comment template is controlled by its meta template.",
+ "Proposal": {
+ "authors": {
+ "Steven Johnson": "steven.johnson@iohk.io"
+ },
+ "business_logic": {
+ "back_end": "Before accepting a new proposal to be published, the backend will ensure:\n\n* The document has been signed by a valid author or collaborator.\n* That the signer of the document was a registered proposer\n* That the document was signed with their proposers key\n* That all listed `collaborators` are registered as proposers.\n* That the document has been signed validly according to the [validation](#validation) rules.",
+ "front_end": "As validity of the documents is currently enforced by the backend, \nthe front end does not need to validate the document has been signed\ncorrectly.\nIt may do so, but it is not required."
+ },
+ "description": "A Proposal is a document which describes a proposed solution or project to\naddress the criteria of a category within a campaign.\n\nThe proposal itself is a draft document, it is not submitted for consideration\nunless a `Proposal Submission Action` is submitted which references it.\n\nProposals themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal is controlled by its template.",
"headers": {
"content type": {
"coseLabel": 3,
"description": "Media Type/s allowed in the Payload",
"format": "Media Type",
"required": "yes",
- "value": "application/schema+json"
+ "value": "application/json"
},
"content-encoding": {
"coseLabel": "content-encoding",
@@ -1242,7 +1254,7 @@
"description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
"exclusive": null,
"format": "Collaborators Reference List",
- "required": "excluded",
+ "required": "optional",
"validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
},
"id": {
@@ -1256,7 +1268,9 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
+ "linked_refs": [
+ "template"
+ ],
"multiple": false,
"required": "yes",
"type": [
@@ -1284,7 +1298,7 @@
"description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
"exclusive": null,
"format": "Version Revocations",
- "required": "excluded",
+ "required": "optional",
"validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
},
"section": {
@@ -1298,7 +1312,10 @@
"description": "Reference to the template used to create and/or validate this document.",
"exclusive": null,
"format": "Document Reference",
- "required": "excluded",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Proposal Template",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
"type": {
@@ -1318,27 +1335,23 @@
},
"notes": [],
"payload": {
- "description": "JSON Schema document which ensures the minimum required functional requirements\nof the Proposal Comment Template are met.\n\nThis ensures that payloads can be reliably interpreted by business logic processes, \nwhile allowing for flexibility to capture extended information.",
- "schema": "https://json-schema.org/draft-07/schema"
+ "description": "Proposal Document drafted for submission to a category of a campaign.\n\nMust be valid according to the schema contained within the \n`Document Reference` from the `template` metadata."
},
"signers": {
"roles": {
- "admin": [
- "Root Admin",
- "Brand Admin"
- ],
- "user": []
+ "user": [
+ "Proposer"
+ ]
},
"update": {
- "author": true
+ "author": true,
+ "collaborators": true
}
},
"type": [
- "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
- "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
- "b679ded3-0e7c-41ba-89f8-da62a17898ea",
"7808d2ba-d511-40af-84e8-c0d1625fdfdc"
],
+ "validation": "The first version of a Proposal *MUST* be signed by the original author.\nIt may optionally be co-signed by any of the listed `collaborators`.\nIt may not be signed by anyone else.\n\nSubsequent Versions can be signed/co-signed by either the Original Author of the first version,\nOR any of the listed `collaborators` in the immediately previous version.\nThis allows any collaborator to update the next version of a document, provided they are still a collaborator.\nIt is valid for a proposal to be signed by a collaborator \nwho is no longer listed as in the `collaborators`\nof the document they are signing, provided they are listed as a collaborator in the immediately previous document version.\nThis allows for a collaborator to make an update to the document which removes themselves\nfrom the `collaborators` list.\n\nAll versions of the document *MUST* list the author as the original author.\nThe Author can not be changed by any document revision.",
"versions": [
{
"changes": "* First Published Version",
@@ -1352,16 +1365,20 @@
}
]
},
- "Proposal Comment Template": {
+ "Proposal Comment": {
"authors": {},
- "description": "## Proposal Comment Template Document\n\nA Proposal Comment Template defines the allowed payload contents of a\nlinked proposal comment.\n\nProposal comments themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal comment is controlled by its template.",
+ "business_logic": {
+ "back_end": "The backend will only validate the document being referenced exists, \nand the integrity of the `ref` and `reply` metadata fields is correct.",
+ "front_end": "Comments are valid for any version of the document, however\nas comments refer to a specific version of a document, they may\nlose context when displayed against the latest version of a document.\nIn these cases, the front end should clearly show that a comment was on\na different version of the document.\n\nIf the front end posts a reply to another comment: \n\n* it should reference the comment being replied to in the `reply` field. \n* The `ref` field must refer to the same document, but can be a different version."
+ },
+ "description": "A Proposal Comment is a document which comments on a referenced Proposal document.\n\nProposal Comments themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal comment is controlled by its template.",
"headers": {
"content type": {
"coseLabel": 3,
"description": "Media Type/s allowed in the Payload",
"format": "Media Type",
"required": "yes",
- "value": "application/schema+json"
+ "value": "application/json"
},
"content-encoding": {
"coseLabel": "content-encoding",
@@ -1392,7 +1409,10 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
+ "linked_refs": [
+ "ref",
+ "template"
+ ],
"multiple": false,
"required": "yes",
"type": [
@@ -1406,28 +1426,34 @@
"description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
"exclusive": null,
"format": "Document Reference",
- "required": "excluded",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Proposal",
"validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
},
"reply": {
"description": "Reference to a Comment document type being referred to.",
"exclusive": null,
"format": "Document Reference",
- "required": "excluded",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "optional",
+ "type": "Proposal Comment",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
},
"revocations": {
"description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
"exclusive": null,
"format": "Version Revocations",
- "required": "excluded",
+ "required": "optional",
"validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
},
"section": {
"description": "A Reference to the original document, or the comment being replied to.",
"exclusive": null,
"format": "Section Reference",
- "required": "excluded",
+ "required": "optional",
"validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
},
"template": {
@@ -1436,8 +1462,8 @@
"format": "Document Reference",
"linked_refs": null,
"multiple": false,
- "required": "optional",
- "type": "Proposal Comment Meta Template",
+ "required": "yes",
+ "type": "Proposal Comment Template",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
"type": {
@@ -1457,25 +1483,23 @@
},
"notes": [],
"payload": {
- "description": "JSON Schema document which defines the content of the Proposal Comments."
+ "description": "JSON Document which must validate against the referenced template."
},
"signers": {
"roles": {
- "admin": [
- "Brand Admin",
- "Campaign Admin"
- ],
- "user": []
+ "user": [
+ "Registered"
+ ]
},
"update": {
"author": true
}
},
"type": [
- "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
"b679ded3-0e7c-41ba-89f8-da62a17898ea",
"7808d2ba-d511-40af-84e8-c0d1625fdfdc"
],
+ "validation": "A comment which is a reply *MUST* reference the same document.\nIt may reference a different version of the document.",
"versions": [
{
"changes": "* First Published Version",
@@ -1489,9 +1513,9 @@
}
]
},
- "Proposal Meta Template": {
+ "Proposal Comment Meta Template": {
"authors": {},
- "description": "## Proposal Meta Template Document\n\nA Proposal Meta Template is used to enforce functional requirements\nare met in any Proposal Template.\n\nThe payload of a proposal template is controlled by its meta template.",
+ "description": "## Proposal Comment Meta Template Document\n\nA Proposal Comment Meta Template is used to enforce functional requirements\nare met in any Proposal Comment Template.\n\nThe payload of a proposal comment template is controlled by its meta template.",
"headers": {
"content type": {
"coseLabel": 3,
@@ -1591,7 +1615,7 @@
},
"notes": [],
"payload": {
- "description": "JSON Schema document which ensures the minimum required functional requirements\nof the Proposal Template are met.\n\nThis ensures that payloads can be reliably interpreted by business logic processes, \nwhile allowing for flexibility to capture extended information.",
+ "description": "JSON Schema document which ensures the minimum required functional requirements\nof the Proposal Comment Template are met.\n\nThis ensures that payloads can be reliably interpreted by business logic processes, \nwhile allowing for flexibility to capture extended information.",
"schema": "https://json-schema.org/draft-07/schema"
},
"signers": {
@@ -1609,6 +1633,7 @@
"type": [
"0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
"0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "b679ded3-0e7c-41ba-89f8-da62a17898ea",
"7808d2ba-d511-40af-84e8-c0d1625fdfdc"
],
"versions": [
@@ -1624,16 +1649,16 @@
}
]
},
- "Proposal Moderation Action": {
+ "Proposal Comment Template": {
"authors": {},
- "description": "A Moderation action performed on a Proposal.",
+ "description": "## Proposal Comment Template Document\n\nA Proposal Comment Template defines the allowed payload contents of a\nlinked proposal comment.\n\nProposal comments themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal comment is controlled by its template.",
"headers": {
"content type": {
"coseLabel": 3,
"description": "Media Type/s allowed in the Payload",
"format": "Media Type",
"required": "yes",
- "value": "application/json"
+ "value": "application/schema+json"
},
"content-encoding": {
"coseLabel": "content-encoding",
@@ -1664,17 +1689,21 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "required": "excluded",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": [
+ "Brand Parameters",
+ "Campaign Parameters",
+ "Category Parameters"
+ ],
"validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
},
"ref": {
"description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
- "multiple": false,
- "required": "yes",
- "type": "Proposal",
+ "required": "excluded",
"validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
},
"reply": {
@@ -1702,7 +1731,10 @@
"description": "Reference to the template used to create and/or validate this document.",
"exclusive": null,
"format": "Document Reference",
- "required": "excluded",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "optional",
+ "type": "Proposal Comment Meta Template",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
"type": {
@@ -1721,20 +1753,285 @@
}
},
"notes": [],
+ "payload": {
+ "description": "JSON Schema document which defines the content of the Proposal Comments."
+ },
"signers": {
"roles": {
- "user": [
- "Registered"
- ]
+ "admin": [
+ "Brand Admin",
+ "Campaign Admin"
+ ],
+ "user": []
},
"update": {
"author": true
}
},
"type": [
- "5e60e623-ad02-4a1b-a1ac-406db978ee48",
- "7808d2ba-d511-40af-84e8-c0d1625fdfdc",
- "a5d232b8-5e03-4117-9afd-be32b878fcdd"
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "b679ded3-0e7c-41ba-89f8-da62a17898ea",
+ "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ ],
+ "versions": [
+ {
+ "changes": "* First Published Version",
+ "modified": "2025-04-04",
+ "version": "0.01"
+ },
+ {
+ "changes": "* Use generalized parameters.",
+ "modified": "2025-05-05",
+ "version": "0.03"
+ }
+ ]
+ },
+ "Proposal Meta Template": {
+ "authors": {},
+ "description": "## Proposal Meta Template Document\n\nA Proposal Meta Template is used to enforce functional requirements\nare met in any Proposal Template.\n\nThe payload of a proposal template is controlled by its meta template.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/schema+json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": [
+ "Brand Parameters",
+ "Campaign Parameters",
+ "Category Parameters"
+ ],
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "payload": {
+ "description": "JSON Schema document which ensures the minimum required functional requirements\nof the Proposal Template are met.\n\nThis ensures that payloads can be reliably interpreted by business logic processes, \nwhile allowing for flexibility to capture extended information.",
+ "schema": "https://json-schema.org/draft-07/schema"
+ },
+ "signers": {
+ "roles": {
+ "admin": [
+ "Root Admin",
+ "Brand Admin"
+ ],
+ "user": []
+ },
+ "update": {
+ "author": true
+ }
+ },
+ "type": [
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ ],
+ "versions": [
+ {
+ "changes": "* First Published Version",
+ "modified": "2025-04-04",
+ "version": "0.01"
+ },
+ {
+ "changes": "* Use generalized parameters.",
+ "modified": "2025-05-05",
+ "version": "0.03"
+ }
+ ]
+ },
+ "Proposal Moderation Action": {
+ "authors": {},
+ "description": "A Moderation action performed on a Proposal.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Proposal",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "signers": {
+ "roles": {
+ "user": [
+ "Registered"
+ ]
+ },
+ "update": {
+ "author": true
+ }
+ },
+ "type": [
+ "5e60e623-ad02-4a1b-a1ac-406db978ee48",
+ "7808d2ba-d511-40af-84e8-c0d1625fdfdc",
+ "a5d232b8-5e03-4117-9afd-be32b878fcdd"
],
"versions": [
{
@@ -1747,10 +2044,656 @@
"Proposal Submission Action": {
"authors": {},
"business_logic": {
- "back_end": "A Submitted proposal with collaborators *MUST* have \na `final` submission by *ALL* listed `collaborators`.\nIf any `collaborator` has not submitted a `final` submission by the deadline, then the proposal \nis not considered `final` and will not be considered in the category it was being submitted to.",
- "front_end": "A proposal with `collaborators` will not be shown as having a confirmed collaborator,\nunless there exists a `draft` or `final` proposal submission from that collaborator.\n\nAny document that lists a collaborator should be highlighted to that collaborator so\nthey can take appropriate action, such as:\n\n* Confirm they are a collaborator by submitting this document as `draft`\n* Agree to being a collaborator on the final submission by submitting this document as `final`\n* Hide themselves from the collaborators list but do not remove themselves by submitting `hide`\n* Remove themselves permanently as a collaborator by publishing a new version with them removed.\n\nTo eliminate the necessity for collaborators to accept collaboration on every version, \nthey will be considered as agreeing to be a collaborator on any version of the document\nthat lists them, if their latest submission is `draft` or `final`.\n\nIf their latest submission on a document is `hide` they should be considered to not\nhave agreed to be a collaborator.\n\n*NOTE* `final` status ONLY applies to the exactly referenced document and version."
+ "back_end": "A Submitted proposal with collaborators *MUST* have \na `final` submission by *ALL* listed `collaborators`.\nIf any `collaborator` has not submitted a `final` submission by the deadline, then the proposal \nis not considered `final` and will not be considered in the category it was being submitted to.",
+ "front_end": "A proposal with `collaborators` will not be shown as having a confirmed collaborator,\nunless there exists a `draft` or `final` proposal submission from that collaborator.\n\nAny document that lists a collaborator should be highlighted to that collaborator so\nthey can take appropriate action, such as:\n\n* Confirm they are a collaborator by submitting this document as `draft`\n* Agree to being a collaborator on the final submission by submitting this document as `final`\n* Hide themselves from the collaborators list but do not remove themselves by submitting `hide`\n* Remove themselves permanently as a collaborator by publishing a new version with them removed.\n\nTo eliminate the necessity for collaborators to accept collaboration on every version, \nthey will be considered as agreeing to be a collaborator on any version of the document\nthat lists them, if their latest submission is `draft` or `final`.\n\nIf their latest submission on a document is `hide` they should be considered to not\nhave agreed to be a collaborator.\n\n*NOTE* `final` status ONLY applies to the exactly referenced document and version."
+ },
+ "description": "## Proposal Submission Action\n\nA Proposal Submission Action is a document which can attempt to either submit a \nparticular version of a proposal into a campaign, or withdraw it.\n\nThe last action on the document ts the action which takes effect at the deadline.\n\nFor multiple collaborators, multiple submission actions can be posted independently, \nbut none of them will take effect until ALL collaborators have posted equivalent actions.\n\nFor example, three collaborators Alice/Bob/Claire can each post one submission action\nfor the same document. \nUnless they all submit the same version of the proposal\nthe proposal will not be seen as submitted.\n\nThe payload is a fixed format.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": [
+ "ref"
+ ],
+ "multiple": false,
+ "required": "yes",
+ "type": [
+ "Brand Parameters",
+ "Campaign Parameters",
+ "Category Parameters"
+ ],
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": true,
+ "required": "yes",
+ "type": "Proposal",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "payload": {
+ "description": "The kind of action is controlled by this payload.\nThe Payload is a JSON Document, and must conform to this schema.\n\nStates:\n\n* `final` : All collaborators must publish a `final` status for the proposal to be `final`.\n* `draft` : Reverses the previous `final` state for a signer and accepts collaborator status to a document. \n* `hide` : Requests the proposal be hidden (not final, but a hidden draft). \n\t\t\t`hide` is only actioned if sent by the author, \n\t\t\tfor a collaborator it identified that they do not wish to be listed as a `collaborator`.",
+ "examples": [
+ {
+ "description": "This document indicates the linked proposal is final and requested to proceed for further consideration.",
+ "example": {
+ "action": "final"
+ },
+ "title": "Final Proposal Submission"
+ },
+ {
+ "description": "This document indicates the linked proposal is no longer final and should not proceed for further consideration.\nIt is also used by collaborators to accept that they are a collaborator on a document.",
+ "example": {
+ "action": "draft"
+ },
+ "title": "Draft Proposal Submission"
+ },
+ {
+ "description": "If submitted by the proposal author the document is hidden, it is still public but not shown as\na proposal being drafted.\nIf submitted by a collaborator, that collaborator is declaring they do not wish to be listed as\na collaborator on the proposal.",
+ "example": {
+ "action": "hide"
+ },
+ "title": "Hidden Proposal Submission"
+ }
+ ],
+ "schema": {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/proposal_submission_action.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "definitions": {
+ "action": {
+ "description": "The action being performed on the Proposal.",
+ "enum": [
+ "final",
+ "draft",
+ "hide"
+ ],
+ "type": "string"
+ }
+ },
+ "description": "Structure of the payload of a Proposal Submission Action.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "action": {
+ "$ref": "#/definitions/action"
+ }
+ },
+ "required": [
+ "action"
+ ],
+ "title": "Proposal Submission Action Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-03-01": [
+ "First Version Created."
+ ]
+ }
+ }
+ },
+ "signers": {
+ "referenced": true,
+ "roles": {
+ "user": [
+ "Proposer"
+ ]
+ },
+ "update": {
+ "author": true,
+ "collaborators": true
+ }
+ },
+ "type": [
+ "5e60e623-ad02-4a1b-a1ac-406db978ee48",
+ "7808d2ba-d511-40af-84e8-c0d1625fdfdc",
+ "78927329-cfd9-4ea1-9c71-0e019b126a65"
+ ],
+ "validation": "No validation is required beyond as defined by:\n\n* [metadata](#metadata) \n* [payload](#payload)\n* [signers](#signers)",
+ "versions": [
+ {
+ "changes": "* First Published Version",
+ "modified": "2025-04-04",
+ "version": "0.01"
+ },
+ {
+ "changes": "* Use generalized parameters.",
+ "modified": "2025-05-05",
+ "version": "0.03"
+ }
+ ]
+ },
+ "Proposal Template": {
+ "authors": {},
+ "description": "## Proposal Template Document\n\nA Proposal Template defines the allowed payload contents of a\nlinked proposal.\n\nProposals themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal is controlled by its template.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/schema+json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": [
+ "template"
+ ],
+ "multiple": false,
+ "required": "yes",
+ "type": [
+ "Brand Parameters",
+ "Campaign Parameters",
+ "Category Parameters"
+ ],
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "optional",
+ "type": "Proposal Meta Template",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "payload": {
+ "description": "JSON Schema document which defines the valid contents of a proposal document."
+ },
+ "signers": {
+ "roles": {
+ "admin": [
+ "Brand Admin",
+ "Campaign Admin"
+ ],
+ "user": []
+ },
+ "update": {
+ "author": true
+ }
+ },
+ "type": [
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ ],
+ "versions": [
+ {
+ "changes": "* First Published Version",
+ "modified": "2025-04-04",
+ "version": "0.01"
+ },
+ {
+ "changes": "* Use generalized parameters.",
+ "modified": "2025-05-05",
+ "version": "0.03"
+ }
+ ]
+ },
+ "Representative Category Profile": {
+ "authors": {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ },
+ "business_logic": {
+ "back_end": "* The backend MUST verify the signer is a 'Representative' and that all referenced documents exist.\n* The system will only consider Representatives with an 'active' status as eligible for delegation.",
+ "front_end": "* Allows a Representative to create or update their profile for a category.\n* The Representative sets their status to 'active' to be discoverable for delegation.\n* The Representative can set their status to 'revoked' to signal they are no longer participating in the category, without having to revoke the document."
+ },
+ "description": "## Representative Category Profile Document\n\nA Representative Category Profile is created to opt in as a Representative for a specific campaign category, the user must have registered as a Representative.\nThe presence of this document signifies the user's intent to participate in that category as a Representative.\n\nThe document's structure is defined by the associated Representative_Category_Profile_Template, which allows an Admin to specify category-specific requirements.\n\nThe payload must contain a 'status' field to indicate if the Representative is active or has revoked their participation.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Category Parameters",
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Representative Profile",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Representative Category Profile Template",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "payload": {
+ "description": "The Representative's profile data for a specific category.\nIts structure is defined by the referenced template document.\nIt MUST contain a 'status' field ('active' or 'revoked') to manage the Representative's participation."
+ },
+ "signers": {
+ "roles": {
+ "user": [
+ "Representative"
+ ]
+ },
+ "update": {
+ "author": true
+ }
+ },
+ "type": [
+ "f1a2b3c4-1111-4abc-8def-2345678901aa"
+ ],
+ "validation": "* The signer MUST be a registered 'Representative'.\n* The 'ref' metadata field MUST point to a valid 'Representative Profile' document.\n* The 'parameters' metadata field MUST point to a valid 'Category Parameters' document.\n* The 'template' metadata field MUST point to a valid 'Representative Category Profile Template' document.\n* The payload MUST be valid against the JSON schema defined in the referenced template.",
+ "versions": [
+ {
+ "changes": "* First Published Version",
+ "modified": "2025-06-19",
+ "version": "0.01"
+ }
+ ]
+ },
+ "Representative Category Profile Template": {
+ "authors": {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ },
+ "business_logic": {
+ "back_end": "* Validate and store the JSON schema that defines the structure for 'Representative Category Profile' documents.",
+ "front_end": ""
+ },
+ "description": "## Representative Category Profile Template\nDefines the JSON schema for a 'Representative Category Profile'.\nThis allows an 'Admin' to specify different profile requirements for each category.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/schema+json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "payload": {
+ "description": "JSON Schema document which defines the valid contents of a Representative Category Profile document.\nThe schema MUST include a 'status' field to indicate if the Representative is active or withdrawn from the category.",
+ "schema": {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/representative_category_profile_template.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": true,
+ "definitions": {
+ "status": {
+ "description": "The Representative's status in this category. 'active' means they are participating, 'revoked' means they have withdrawn.",
+ "enum": [
+ "active",
+ "revoked"
+ ],
+ "type": "string"
+ }
+ },
+ "description": "This schema is defined by an Admin to specify the required properties for a user opting in as a Representative within a specific campaign category. It outlines the structure of a Representative's profile for that category and supports the addition of custom properties as needed by the Admin. The status field is mandatory and cannot be removed or modified by the Admin.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "status": {
+ "$ref": "#/definitions/status"
+ }
+ },
+ "required": [
+ "status"
+ ],
+ "title": "Representative Category Profile Template Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
+ },
+ "signers": {
+ "roles": {
+ "admin": [
+ "Brand Admin",
+ "Campaign Admin"
+ ],
+ "user": [
+ "Registered"
+ ]
+ },
+ "update": {
+ "author": true
+ }
+ },
+ "type": [
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "f1a2b3c4-1111-4abc-8def-2345678901aa"
+ ],
+ "validation": "* The signer MUST be a registered 'Admin'.\n* The payload MUST be a valid JSON schema.\n* The schema MUST include a 'status' field.",
+ "versions": [
+ {
+ "changes": " * First Published Version",
+ "modified": "2025-06-19",
+ "version": "0.01"
+ }
+ ]
+ },
+ "Representative Profile": {
+ "authors": {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ },
+ "business_logic": {
+ "back_end": "* Validate Representative profile data against the referenced 'Representative_Profile_Template' and store it.\n* This global profile is the foundational document referenced by all of the Representative's category-specific profiles.",
+ "front_end": "* Display and allow editing of the Representative's core profile fields.\n* This profile serves as the central hub for a Representative's identity."
},
- "description": "## Proposal Submission Action\n\nA Proposal Submission Action is a document which can attempt to either submit a \nparticular version of a proposal into a campaign, or withdraw it.\n\nThe last action on the document ts the action which takes effect at the deadline.\n\nFor multiple collaborators, multiple submission actions can be posted independently, \nbut none of them will take effect until ALL collaborators have posted equivalent actions.\n\nFor example, three collaborators Alice/Bob/Claire can each post one submission action\nfor the same document. \nUnless they all submit the same version of the proposal\nthe proposal will not be seen as submitted.\n\nThe payload is a fixed format.",
+ "description": "## Representative Profile Document\n\nA Representative-specific profile, extending the minimal profile with Representative-specific fields.",
"headers": {
"content type": {
"coseLabel": 3,
@@ -1788,26 +2731,142 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": [
- "ref"
- ],
- "multiple": false,
- "required": "yes",
- "type": [
- "Brand Parameters",
- "Campaign Parameters",
- "Category Parameters"
- ],
+ "required": "excluded",
"validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
},
"ref": {
"description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
"exclusive": null,
"format": "Document Reference",
+ "required": "excluded",
+ "validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
+ },
+ "reply": {
+ "description": "Reference to a Comment document type being referred to.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe `ref` of the `reply` document must be the same as\nthe original comment document."
+ },
+ "revocations": {
+ "description": "A document may include a list of any prior versions which are considered to be revoked.\nOnly the revocation list in the latest version of the document applies.\nRevoked documents are flagged as no longer valid, and should not be displayed.\nAs a special case, if the revocations are set to `true` then all versions of the document\nare revoked, including the latest document.\n\nIn this case, when the latest document is revoked, the payload may be empty.\nAny older document that has `revocations` set to `true` is always to be filtered\nand its payload is to be assumed to be invalid.\n\nThis allows for an entire document and any/all published versions to be revoked.\nA new version of the document that is published after this, may reinstate prior\ndocument versions, by not listing them as revoked. \nHowever, any document where revocations was set `true` can never be reinstated.",
+ "exclusive": null,
+ "format": "Version Revocations",
+ "required": "excluded",
+ "validation": "If the field is `true` the payload may be absent or invalid.\nSuch documents may never be submitted."
+ },
+ "section": {
+ "description": "A Reference to the original document, or the comment being replied to.",
+ "exclusive": null,
+ "format": "Section Reference",
+ "required": "excluded",
+ "validation": "For a non-reply this must be a valid section reference into the referenced document.\nFor a reply, this must be a valid section reference into the comment being replied to."
+ },
+ "template": {
+ "description": "Reference to the template used to create and/or validate this document.",
+ "exclusive": null,
+ "format": "Document Reference",
"linked_refs": null,
- "multiple": true,
+ "multiple": false,
"required": "yes",
- "type": "Proposal",
+ "type": "Representative Profile Template",
+ "validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
+ },
+ "type": {
+ "description": "The document TYPE.",
+ "exclusive": null,
+ "format": "Document Type",
+ "required": "yes",
+ "validation": "**MUST** be a known document type."
+ },
+ "ver": {
+ "description": "The unique version of the document.\nThe first version of the document must set `ver` == `id`",
+ "exclusive": null,
+ "format": "Document Ver",
+ "required": "yes",
+ "validation": "The document version must always be >= the document ID."
+ }
+ },
+ "notes": [],
+ "payload": {
+ "description": "The Representative profile payload contains all base profile fields and Representative-specific fields.\nIts structure is defined by the referenced Representative Profile Template."
+ },
+ "signers": {
+ "roles": {
+ "user": [
+ "Representative"
+ ]
+ },
+ "update": {
+ "author": true
+ }
+ },
+ "type": [
+ "e3f2c1b4-7890-4abc-8def-2345678901ef"
+ ],
+ "validation": " * The signer MUST be a registered 'Representative'.\n * The payload MUST be valid against the JSON schema defined in the referenced 'Representative Profile Template'.",
+ "versions": [
+ {
+ "changes": "* First Published Version",
+ "modified": "2025-06-19",
+ "version": "0.01"
+ }
+ ]
+ },
+ "Representative Profile Template": {
+ "authors": {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ },
+ "business_logic": {
+ "back_end": "* Validate and store the JSON schema that defines the structure for all 'Representative Profile' documents.\n* The schema MUST extend the base 'Profile' schema with Representative-specific fields.",
+ "front_end": ""
+ },
+ "description": "## Representative Profile Template\nDefines the JSON schema for a 'Representative Profile'.\nThis template allows an 'Admin' to enforce a specific structure and set of constraints for Representative profiles.",
+ "headers": {
+ "content type": {
+ "coseLabel": 3,
+ "description": "Media Type/s allowed in the Payload",
+ "format": "Media Type",
+ "required": "yes",
+ "value": "application/json"
+ },
+ "content-encoding": {
+ "coseLabel": "content-encoding",
+ "description": "Supported HTTP Encodings of the Payload.\nIf no compression or encoding is used, then this field must not be present.",
+ "format": "HTTP Content Encoding",
+ "required": "optional",
+ "value": [
+ "br"
+ ]
+ }
+ },
+ "metadata": {
+ "collaborators": {
+ "description": "A list of collaborators who may also publish updates to versions of this document.\nThis should include all parties who have not signed this document directly.\n\nEvery subsequent version can amend the collaborators list.\nHowever, the initial Author can never be removed from being able to\npublish a new version of the document.",
+ "exclusive": null,
+ "format": "Collaborators Reference List",
+ "required": "excluded",
+ "validation": "This list does not imply these collaborators have consented to collaborate, only that the author/s\nare permitting these potential collaborators to participate in the drafting and submission process.\nHowever, any document submission referencing a proposal MUST be signed by all collaborators in\naddition to the author."
+ },
+ "id": {
+ "description": "Document ID, created the first time the document is created.\nThis must be a properly created UUIDv7 which contains the \ntimestamp of when the document was created.",
+ "exclusive": null,
+ "format": "Document Id",
+ "required": "yes",
+ "validation": "IF `ver` does not == `id` then a document with \n`id` and `ver` being equal *MUST* exist."
+ },
+ "parameters": {
+ "description": "A reference to the Parameters Document this document lies under.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
+ "validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
+ },
+ "ref": {
+ "description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
+ "exclusive": null,
+ "format": "Document Reference",
+ "required": "excluded",
"validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
},
"reply": {
@@ -1855,23 +2914,12 @@
},
"notes": [],
"payload": {
- "description": "The kind of action is controlled by this payload.\nThe Payload is a JSON Document, and must conform to this schema.\n\nStates:\n\n* `final` : All collaborators must publish a `final` status for the proposal to be `final`.\n* `draft` : Reverses the previous `final` state for a signer and accepts collaborator status to a document. \n* `hide` : Requests the proposal be hidden (not final, but a hidden draft). \n\t\t\t`hide` is only actioned if sent by the author, \n\t\t\tfor a collaborator it identified that they do not wish to be listed as a `collaborator`.",
+ "description": "JSON Schema document which defines the valid contents of a Representative profile document.",
"schema": {
- "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/proposal_submission_action.schema.json",
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/drep_profile_template.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
- "definitions": {
- "action": {
- "description": "The action being performed on the Proposal.",
- "enum": [
- "final",
- "draft",
- "hide"
- ],
- "type": "string"
- }
- },
- "description": "Structure of the payload of a Proposal Submission Action.",
+ "description": "Schema for a Representative profile document template, extending the base profile with Representative-specific fields as defined by the Admin.",
"maintainers": [
{
"name": "Catalyst Team",
@@ -1879,63 +2927,96 @@
}
],
"properties": {
- "action": {
- "$ref": "#/definitions/action"
+ "avatar": {
+ "$ref": "common_definitions.schema.json#/definitions/ipfsUrl"
+ },
+ "avatar_required": {
+ "enum": [
+ "yes",
+ "optional",
+ "excluded"
+ ],
+ "type": "string"
+ },
+ "bio": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "social_links": {
+ "items": {
+ "$ref": "common_definitions.schema.json#/definitions/url"
+ },
+ "type": "array"
+ },
+ "tags": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "website": {
+ "$ref": "common_definitions.schema.json#/definitions/url"
}
},
"required": [
- "action"
+ "name",
+ "bio",
+ "avatar",
+ "website",
+ "social_links",
+ "tags",
+ "avatar_required"
],
- "title": "Proposal Submission Action Payload Schema",
+ "title": "Representative Profile Template Payload Schema",
"type": "object",
"x-changelog": {
- "2025-03-01": [
+ "2025-06-19": [
"First Version Created."
]
}
}
},
"signers": {
- "referenced": true,
"roles": {
"user": [
- "Proposer"
+ "Registered"
]
},
"update": {
- "author": true,
- "collaborators": true
+ "author": true
}
},
"type": [
- "5e60e623-ad02-4a1b-a1ac-406db978ee48",
- "7808d2ba-d511-40af-84e8-c0d1625fdfdc",
- "78927329-cfd9-4ea1-9c71-0e019b126a65"
+ "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
+ "e3f2c1b4-7890-4abc-8def-2345678901ef"
],
- "validation": "No validation is required beyond as defined by:\n\n* [metadata](#metadata) \n* [payload](#payload)\n* [signers](#signers)",
+ "validation": "* The signer MUST be a registered 'Admin'.\n* The payload MUST be a valid JSON schema.",
"versions": [
{
- "changes": "* First Published Version",
- "modified": "2025-04-04",
+ "changes": " * First Published Version",
+ "modified": "2025-06-19",
"version": "0.01"
- },
- {
- "changes": "* Use generalized parameters.",
- "modified": "2025-05-05",
- "version": "0.03"
}
]
},
- "Proposal Template": {
- "authors": {},
- "description": "## Proposal Template Document\n\nA Proposal Template defines the allowed payload contents of a\nlinked proposal.\n\nProposals themselves are intentionally general, however they may be\nlinked to a brand/campaign or category via the template used by the proposal.\n\nThe payload of a proposal is controlled by its template.",
+ "Voter Representative Delegation": {
+ "authors": {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ },
+ "business_logic": {
+ "back_end": "* Verifies that the voter and Representative are valid and registered for the category.\n* Records the delegation of voting power from the voter to the Representative.",
+ "front_end": "* Allows a voter to select a Representative from a list of eligible candidates for a category.\n* The voter signs this document to confirm their delegation choice."
+ },
+ "description": "## Voter Representative Delegation\nA signed document that allows a 'Voter' to delegate their voting power to a 'Representative' for a specific category.",
"headers": {
"content type": {
"coseLabel": 3,
"description": "Media Type/s allowed in the Payload",
"format": "Media Type",
"required": "yes",
- "value": "application/schema+json"
+ "value": "application/json"
},
"content-encoding": {
"coseLabel": "content-encoding",
@@ -1966,23 +3047,20 @@
"description": "A reference to the Parameters Document this document lies under.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": [
- "template"
- ],
+ "linked_refs": null,
"multiple": false,
"required": "yes",
- "type": [
- "Brand Parameters",
- "Campaign Parameters",
- "Category Parameters"
- ],
+ "type": "Category Parameters",
"validation": "In addition to the validation performed for `Document Reference` type fields: \n\n* Any linked referenced document that includes a `parameters` metadata must match the \n`parameters` of the referencing document."
},
"ref": {
"description": "Reference to a Linked Document or Documents. \nThis is the primary hierarchical reference to a related document.\t\t\t\n\nIf a reference is defined as required, there must be at least 1 reference specified.\nSome documents allow multiple references, and they are documented as required.\n\nThe document reference serves two purposes:\n \n1. It ensures that the document referenced by an ID/Version is not substituted.\n\tIn other words, that the document intended to be referenced, is actually referenced.\n2. It Allows the document to be unambiguously located in decentralized storage systems.\n\nThere can be any number of Document Locations in any reference.\nThe currently defined locations are:\n\n* `cid` : A CBOR Encoded IPLD Content Identifier ( AKA an IPFS CID ).\n* Others may be added when further storage mechanisms are defined.\n\nThe document location does not guarantee that the document is actually stored.\nIt only defines that if it were stored, this is the identifier\nthat is required to retrieve it.\nTherefore it is required that Document References\nare unique and reproducible, given a documents contents.",
"exclusive": null,
"format": "Document Reference",
- "required": "excluded",
+ "linked_refs": null,
+ "multiple": false,
+ "required": "yes",
+ "type": "Representative Category Profile",
"validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
},
"reply": {
@@ -2010,10 +3088,7 @@
"description": "Reference to the template used to create and/or validate this document.",
"exclusive": null,
"format": "Document Reference",
- "linked_refs": null,
- "multiple": false,
- "required": "optional",
- "type": "Proposal Meta Template",
+ "required": "excluded",
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
"type": {
@@ -2033,39 +3108,65 @@
},
"notes": [],
"payload": {
- "description": "JSON Schema document which defines the valid contents of a proposal document."
+ "description": " A minimal payload indicating the intended status of the delegation.\n 'active' creates or affirms the delegation.\n 'revoked' withdraws the delegation.",
+ "schema": {
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/voter_representative_delegation.schema.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "additionalProperties": false,
+ "description": "This payload is submitted when a voter delegates to a Representative, with the voter as the signer and the Representative’s category profile as the referenced document; to revoke the delegation, the voter can publish again with the status set to revoked.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "properties": {
+ "status": {
+ "description": "The status of the delegation. 'active' signifies delegation (set by default), 'revoked' signifies withdrawal of delegation.",
+ "enum": [
+ "active",
+ "revoked"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "status"
+ ],
+ "title": "Voter Representative Delegation Payload Schema",
+ "type": "object",
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ }
+ }
},
"signers": {
"roles": {
- "admin": [
- "Brand Admin",
- "Campaign Admin"
- ],
- "user": []
+ "user": [
+ "Registered"
+ ]
},
"update": {
"author": true
}
},
"type": [
- "0ce8ab38-9258-4fbc-a62e-7faa6e58318f",
- "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ "f1a2b3c4-3333-4abc-8def-2345678901cc"
],
+ "validation": "* The signer MUST be a registered 'Voter'.\n* The 'ref' metadata field MUST point to a valid 'Representative Category Profile'.\n* The payload MUST be empty.",
"versions": [
{
"changes": "* First Published Version",
- "modified": "2025-04-04",
+ "modified": "2025-06-19",
"version": "0.01"
- },
- {
- "changes": "* Use generalized parameters.",
- "modified": "2025-05-05",
- "version": "0.03"
}
]
}
},
"documentationLinks": {
+ "CBOR-LFD-ENCODING": "https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.3",
"CBOR-TAG-37": "https://github.com/lucas-clemente/cbor-specs/blob/master/uuid.md",
"CBOR-TAG-42": "https://github.com/ipld/cid-cbor/",
"CC-BY-4.0": "https://creativecommons.org/licenses/by/4.0/legalcode",
@@ -2097,6 +3198,7 @@
"linkAKA": {
"BROTLI": "RFC7932",
"CBOR": "RFC8949",
+ "CBOR Deterministic Encoding": "CBOR-LFD-ENCODING",
"CBOR Encoded IPLD Content Identifier": "CBOR-TAG-42",
"CDDL": "RFC8610",
"COSE": "RFC9052",
@@ -2161,7 +3263,14 @@
"Brand Parameters",
"Campaign Parameters",
"Category Parameters",
- "Decision Parameters"
+ "Decision Parameters",
+ "Profile",
+ "Profile Template",
+ "Representative Profile Template",
+ "Representative Category Profile Template",
+ "Representative Profile",
+ "Representative Category Profile",
+ "Voter Representative Delegation"
],
"validation": "The following must be true for a valid reference:\n\n* The Referenced Document **MUST** Exist\n* Every value in the `document_locator` must consistently reference the exact same document.\n* The `document_id` and `document_ver` **MUST** match the values in the referenced document."
},
@@ -2202,7 +3311,10 @@
"Proposal Meta Template",
"Proposal Template",
"Proposal Comment Meta Template",
- "Proposal Comment Template"
+ "Proposal Comment Template",
+ "Profile Template",
+ "Representative Profile Template",
+ "Representative Category Profile Template"
],
"validation": "In addition to the validation performed for `Document Reference` type fields, \nThe document payload is not valid if it does not validate completely against the referenced template."
},
diff --git a/specs/signed_docs/authors_copyright.cue b/specs/signed_docs/authors_copyright.cue
index c354a5b10e..2d9a8314d8 100644
--- a/specs/signed_docs/authors_copyright.cue
+++ b/specs/signed_docs/authors_copyright.cue
@@ -60,5 +60,16 @@ copyright: #copyrightNotice & {
* Use generalized parameters.
"""
},
+ {
+ version: "0.04"
+ modified: "2025-05-30"
+ changes: """
+ * Improve and make document serialization more repeatable, and stricter.
+ * TODO: Define Systems parameters
+ * TODO: Define DReps documents.
+ * TODO: Define Proposer Profiles.
+ * TODO: Define Role 0 Profile.
+ """
+ },
]
}
diff --git a/specs/signed_docs/docs/all.cue b/specs/signed_docs/docs/all.cue
index a0859b3102..e9eaea2441 100644
--- a/specs/signed_docs/docs/all.cue
+++ b/specs/signed_docs/docs/all.cue
@@ -7,16 +7,20 @@ import (
// Named Type UUIDs for easier definitions/references
_allDocTypes: {
- "Template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f"
- "Proposal": "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
- "Comment": "b679ded3-0e7c-41ba-89f8-da62a17898ea"
- "Action": "5e60e623-ad02-4a1b-a1ac-406db978ee48"
- "SubmissionAction": "78927329-cfd9-4ea1-9c71-0e019b126a65"
- "ModerationAction": "a5d232b8-5e03-4117-9afd-be32b878fcdd"
- "Brand": "ebcabeeb-5bc5-4f95-91e8-cab8ca724172"
- "Campaign": "5ef32d5d-f240-462c-a7a4-ba4af221fa23"
- "Category": "818938c3-3139-4daa-afe6-974c78488e95"
- "Decision": "788ff4c6-d65a-451f-bb33-575fe056b411"
+ "Template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f"
+ "Proposal": "7808d2ba-d511-40af-84e8-c0d1625fdfdc"
+ "Comment": "b679ded3-0e7c-41ba-89f8-da62a17898ea"
+ "Action": "5e60e623-ad02-4a1b-a1ac-406db978ee48"
+ "SubmissionAction": "78927329-cfd9-4ea1-9c71-0e019b126a65"
+ "ModerationAction": "a5d232b8-5e03-4117-9afd-be32b878fcdd"
+ "Brand": "ebcabeeb-5bc5-4f95-91e8-cab8ca724172"
+ "Campaign": "5ef32d5d-f240-462c-a7a4-ba4af221fa23"
+ "Category": "818938c3-3139-4daa-afe6-974c78488e95"
+ "Decision": "788ff4c6-d65a-451f-bb33-575fe056b411"
+ "Profile": "1b70f611-518d-479e-be73-11b5e9cb68a5"
+ "RepresentativeProfile": "e3f2c1b4-7890-4abc-8def-2345678901ef"
+ "RepresentativeCategoryProfile": "f1a2b3c4-1111-4abc-8def-2345678901aa"
+ "VoterRepresentativeDelegation": "f1a2b3c4-3333-4abc-8def-2345678901cc"
}
// Source of truth for ALL Document Types and their matching UUID's.
@@ -78,7 +82,30 @@ _allDocs: {
"Decision Parameters": [
_allDocTypes["Decision"],
]
-
+ "Profile": [
+ _allDocTypes["Profile"],
+ ]
+ "Profile Template": [
+ _allDocTypes["Template"],
+ _allDocTypes["Profile"],
+ ]
+ "Representative Profile Template": [
+ _allDocTypes["Template"],
+ _allDocTypes["RepresentativeProfile"],
+ ]
+ "Representative Category Profile Template": [
+ _allDocTypes["Template"],
+ _allDocTypes["RepresentativeCategoryProfile"],
+ ]
+ "Representative Profile": [
+ _allDocTypes["RepresentativeProfile"],
+ ]
+ "Representative Category Profile": [
+ _allDocTypes["RepresentativeCategoryProfile"],
+ ]
+ "Voter Representative Delegation": [
+ _allDocTypes["VoterRepresentativeDelegation"],
+ ]
}
// Document Cluster Definition
diff --git a/specs/signed_docs/docs/payload_schemas/common_definitions.schema.json b/specs/signed_docs/docs/payload_schemas/common_definitions.schema.json
new file mode 100644
index 0000000000..5234074662
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/common_definitions.schema.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/common_definitions.schema.json",
+ "title": "Common Definitions",
+ "description": "Commonly used data type definitions for signed document schemas.",
+ "definitions": {
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+"
+ },
+ "ipfsUrl": {
+ "type": "string",
+ "pattern": "^ipfs://.+"
+ },
+ "uuid": {
+ "type": "string",
+ "format": "uuid"
+ },
+ "date_yyyymmdd": {
+ "type": "string",
+ "pattern": "^\\\\d{4}-\\\\d{2}-\\\\d{2}$"
+ },
+ "timestamp_rfc3339": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/profile_template.schema.json b/specs/signed_docs/docs/payload_schemas/profile_template.schema.json
new file mode 100644
index 0000000000..d7a58a5e22
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/profile_template.schema.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/profile_template.schema.json",
+ "title": "Profile Template Payload Schema",
+ "description": "Schema for a profile document template for any Catalyst actor.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ },
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": { "type": "string" },
+ "bio": { "type": "string" }
+ },
+ "required": ["name", "bio"]
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/proposal_submission_action.draft.example.json b/specs/signed_docs/docs/payload_schemas/proposal_submission_action.draft.example.json
new file mode 100644
index 0000000000..747d3b6ec7
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/proposal_submission_action.draft.example.json
@@ -0,0 +1,3 @@
+{
+ "action": "draft"
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/proposal_submission_action.final.example.json b/specs/signed_docs/docs/payload_schemas/proposal_submission_action.final.example.json
new file mode 100644
index 0000000000..bfbf15e59d
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/proposal_submission_action.final.example.json
@@ -0,0 +1,3 @@
+{
+ "action": "final"
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/proposal_submission_action.hide.example.json b/specs/signed_docs/docs/payload_schemas/proposal_submission_action.hide.example.json
new file mode 100644
index 0000000000..e6f023094f
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/proposal_submission_action.hide.example.json
@@ -0,0 +1,3 @@
+{
+ "action": "hide"
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/representative_category_profile_template.schema.json b/specs/signed_docs/docs/payload_schemas/representative_category_profile_template.schema.json
new file mode 100644
index 0000000000..6f5a257632
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/representative_category_profile_template.schema.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/representative_category_profile_template.schema.json",
+ "title": "Representative Category Profile Template Payload Schema",
+ "description": "This schema is defined by an Admin to specify the required properties for a user opting in as a Representative within a specific campaign category. It outlines the structure of a Representative's profile for that category and supports the addition of custom properties as needed by the Admin. The status field is mandatory and cannot be removed or modified by the Admin.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ },
+ "type": "object",
+ "additionalProperties": true,
+ "definitions": {
+ "status": {
+ "type": "string",
+ "enum": ["active", "revoked"],
+ "description": "The Representative's status in this category. 'active' means they are participating, 'revoked' means they have withdrawn."
+ }
+ },
+ "properties": {
+ "status": {
+ "$ref": "#/definitions/status"
+ }
+ },
+ "required": ["status"]
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/representative_profile_template.schema.json b/specs/signed_docs/docs/payload_schemas/representative_profile_template.schema.json
new file mode 100644
index 0000000000..b2747149a6
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/representative_profile_template.schema.json
@@ -0,0 +1,43 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/drep_profile_template.schema.json",
+ "title": "Representative Profile Template Payload Schema",
+ "description": "Schema for a Representative profile document template, extending the base profile with Representative-specific fields as defined by the Admin.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ },
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": { "type": "string" },
+ "bio": { "type": "string" },
+ "avatar": { "$ref": "common_definitions.schema.json#/definitions/ipfsUrl" },
+ "website": { "$ref": "common_definitions.schema.json#/definitions/url" },
+ "social_links": {
+ "type": "array",
+ "items": { "$ref": "common_definitions.schema.json#/definitions/url" }
+ },
+ "tags": {
+ "type": "array",
+ "items": { "type": "string" }
+ },
+ "avatar_required": { "type": "string", "enum": ["yes", "optional", "excluded"] }
+ },
+ "required": [
+ "name",
+ "bio",
+ "avatar",
+ "website",
+ "social_links",
+ "tags",
+ "avatar_required"
+ ]
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/payload_schemas/voter_representative_delegation.schema.json b/specs/signed_docs/docs/payload_schemas/voter_representative_delegation.schema.json
new file mode 100644
index 0000000000..9d2f2b9a7a
--- /dev/null
+++ b/specs/signed_docs/docs/payload_schemas/voter_representative_delegation.schema.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-libs/refs/heads/main/specs/signed_docs/docs/payload_schemas/voter_representative_delegation.schema.json",
+ "title": "Voter Representative Delegation Payload Schema",
+ "description": "This payload is submitted when a voter delegates to a Representative, with the voter as the signer and the Representative’s category profile as the referenced document; to revoke the delegation, the voter can publish again with the status set to revoked.",
+ "maintainers": [
+ {
+ "name": "Catalyst Team",
+ "url": "https://projectcatalyst.io/"
+ }
+ ],
+ "x-changelog": {
+ "2025-06-19": [
+ "First Version Created."
+ ]
+ },
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "status": {
+ "type": "string",
+ "enum": ["active", "revoked"],
+ "description": "The status of the delegation. 'active' signifies delegation (set by default), 'revoked' signifies withdrawal of delegation."
+ }
+ },
+ "required": ["status"]
+}
\ No newline at end of file
diff --git a/specs/signed_docs/docs/profile.cue b/specs/signed_docs/docs/profile.cue
new file mode 100644
index 0000000000..3b8817cf26
--- /dev/null
+++ b/specs/signed_docs/docs/profile.cue
@@ -0,0 +1,59 @@
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Profile": {
+ description: """
+ ## Profile Document
+
+ A minimal user profile that provides basic information about a user.
+ Its structure is defined by the referenced Profile Template.
+ It is used as a base for more specific profiles like the Representative Profile.
+ """
+ validation: """
+ * The signer must be a registered 'User'.
+ * The payload must be valid against the JSON schema defined in the referenced 'Profile Template'.
+ """
+ business_logic: {
+ front_end: """
+ * Display the user's profile information.
+ * Allow a user to edit their own profile data.
+ """
+ back_end: """
+ * Validate and store profile data against the referenced 'Profile_Template'.
+ * This profile serves as the base document for a user.
+ Its scope can be extended to create more specific profiles.
+ """
+ }
+ metadata: {
+ template: {
+ required: "yes"
+ type: "Profile Template"
+ }
+ }
+ payload: {
+ description: """
+ The profile payload contains all base profile fields.
+ Its structure is defined by the referenced Profile Template.
+ """
+ }
+ signers: {
+ roles: {
+ user: [
+ "Registered",
+ ]
+ }
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/docs/profile_template.cue b/specs/signed_docs/docs/profile_template.cue
new file mode 100644
index 0000000000..2fece1a71c
--- /dev/null
+++ b/specs/signed_docs/docs/profile_template.cue
@@ -0,0 +1,53 @@
+@extern(embed)
+
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Profile Template": {
+ description: """
+ ## Profile Template Document
+
+ Defines the allowed payload contents and constraints for a generic user profile.
+ """
+ validation: """
+ * The signer MUST be a registered 'Admin'.
+ * The payload MUST be a valid JSON schema.
+ * The schema SHOULD define a minimal set of profile fields (e.g., name, bio).
+ """
+ business_logic: {
+ front_end: """
+ """
+ back_end: """
+ * Validate and store the JSON schema that defines the structure for all 'Profile' documents.
+ """
+ }
+ metadata: {
+ // Add any template-specific metadata here if needed
+ }
+ payload: {
+ description: """
+ JSON Schema document which defines the valid contents of a profile document.
+ """
+ schema: _ @embed(file="payload_schemas/profile_template.schema.json")
+ }
+ signers: {
+ roles: {
+ admin: [
+ "Brand Admin",
+ ]
+ }
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/docs/proposal_submission_action.cue b/specs/signed_docs/docs/proposal_submission_action.cue
index 1db5557848..6d09f9a7fd 100644
--- a/specs/signed_docs/docs/proposal_submission_action.cue
+++ b/specs/signed_docs/docs/proposal_submission_action.cue
@@ -92,6 +92,33 @@ docs: #DocumentDefinitions & {
for a collaborator it identified that they do not wish to be listed as a `collaborator`.
"""
schema: _ @embed(file="payload_schemas/proposal_submission_action.schema.json")
+ examples: [
+ {
+ title: "Final Proposal Submission"
+ description: """
+ This document indicates the linked proposal is final and requested to proceed for further consideration.
+ """
+ example: _ @embed(file="payload_schemas/proposal_submission_action.final.example.json")
+ },
+ {
+ title: "Draft Proposal Submission"
+ description: """
+ This document indicates the linked proposal is no longer final and should not proceed for further consideration.
+ It is also used by collaborators to accept that they are a collaborator on a document.
+ """
+ example: _ @embed(file="payload_schemas/proposal_submission_action.draft.example.json")
+ },
+ {
+ title: "Hidden Proposal Submission"
+ description: """
+ If submitted by the proposal author the document is hidden, it is still public but not shown as
+ a proposal being drafted.
+ If submitted by a collaborator, that collaborator is declaring they do not wish to be listed as
+ a collaborator on the proposal.
+ """
+ example: _ @embed(file="payload_schemas/proposal_submission_action.hide.example.json")
+ },
+ ]
}
"signers": {
diff --git a/specs/signed_docs/docs/representative_category_profile.cue b/specs/signed_docs/docs/representative_category_profile.cue
new file mode 100644
index 0000000000..bcf848e0bb
--- /dev/null
+++ b/specs/signed_docs/docs/representative_category_profile.cue
@@ -0,0 +1,65 @@
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Representative Category Profile": {
+ description: "## Representative Category Profile Document\n\nA Representative Category Profile is created to opt in as a Representative for a specific campaign category, the user must have registered as a Representative.\nThe presence of this document signifies the user's intent to participate in that category as a Representative.\n\nThe document's structure is defined by the associated Representative_Category_Profile_Template, which allows an Admin to specify category-specific requirements.\n\nThe payload must contain a 'status' field to indicate if the Representative is active or has revoked their participation."
+ validation: """
+ * The signer MUST be a registered 'Representative'.
+ * The 'ref' metadata field MUST point to a valid 'Representative Profile' document.
+ * The 'parameters' metadata field MUST point to a valid 'Category Parameters' document.
+ * The 'template' metadata field MUST point to a valid 'Representative Category Profile Template' document.
+ * The payload MUST be valid against the JSON schema defined in the referenced template.
+ """
+ business_logic: {
+ front_end: """
+ * Allows a Representative to create or update their profile for a category.
+ * The Representative sets their status to 'active' to be discoverable for delegation.
+ * The Representative can set their status to 'revoked' to signal they are no longer participating in the category, without having to revoke the document.
+ """
+ back_end: """
+ * The backend MUST verify the signer is a 'Representative' and that all referenced documents exist.
+ * The system will only consider Representatives with an 'active' status as eligible for delegation.
+ """
+ }
+ metadata: {
+ ref: {
+ required: "yes"
+ type: "Representative Profile"
+ }
+ parameters: {
+ required: "yes"
+ type: "Category Parameters"
+ }
+ template: {
+ required: "yes"
+ type: "Representative Category Profile Template"
+ }
+ }
+ payload: {
+ description: """
+ The Representative's profile data for a specific category.
+ Its structure is defined by the referenced template document.
+ It MUST contain a 'status' field ('active' or 'revoked') to manage the Representative's participation.
+ """
+ }
+ signers: {
+ roles: {
+ user: [
+ "Representative",
+ ]
+ }
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/docs/representative_category_profile_template.cue b/specs/signed_docs/docs/representative_category_profile_template.cue
new file mode 100644
index 0000000000..805f3e144a
--- /dev/null
+++ b/specs/signed_docs/docs/representative_category_profile_template.cue
@@ -0,0 +1,56 @@
+@extern(embed)
+
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Representative Category Profile Template": {
+ description: "## Representative Category Profile Template\nDefines the JSON schema for a 'Representative Category Profile'.\nThis allows an 'Admin' to specify different profile requirements for each category."
+ validation: """
+ * The signer MUST be a registered 'Admin'.
+ * The payload MUST be a valid JSON schema.
+ * The schema MUST include a 'status' field.
+ """
+ business_logic: {
+ front_end: """
+ """
+ back_end: """
+ * Validate and store the JSON schema that defines the structure for 'Representative Category Profile' documents.
+ """
+ }
+ metadata: {
+ // Add any template-specific metadata here if needed
+ }
+ headers: {
+ "content type": {
+ value: "application/schema+json"
+ }
+ }
+ payload: {
+ description: """
+ JSON Schema document which defines the valid contents of a Representative Category Profile document.
+ The schema MUST include a 'status' field to indicate if the Representative is active or withdrawn from the category.
+ """
+ schema: _ @embed(file="payload_schemas/representative_category_profile_template.schema.json")
+ }
+ signers: {
+ roles: {
+ admin: [
+ "Brand Admin",
+ "Campaign Admin",
+ ]
+ }
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/docs/representative_profile.cue b/specs/signed_docs/docs/representative_profile.cue
new file mode 100644
index 0000000000..3ba9658e3c
--- /dev/null
+++ b/specs/signed_docs/docs/representative_profile.cue
@@ -0,0 +1,58 @@
+@extern(embed)
+
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Representative Profile": {
+ description: """
+ ## Representative Profile Document
+
+ A Representative-specific profile, extending the minimal profile with Representative-specific fields.
+ """
+ validation: """
+ * The signer MUST be a registered 'Representative'.
+ * The payload MUST be valid against the JSON schema defined in the referenced 'Representative Profile Template'.
+ """
+ business_logic: {
+ front_end: """
+ * Display and allow editing of the Representative's core profile fields.
+ * This profile serves as the central hub for a Representative's identity.
+ """
+ back_end: """
+ * Validate Representative profile data against the referenced 'Representative_Profile_Template' and store it.
+ * This global profile is the foundational document referenced by all of the Representative's category-specific profiles.
+ """
+ }
+ metadata: {
+ template: {
+ required: "yes"
+ type: "Representative Profile Template"
+ }
+ }
+ payload: {
+ description: """
+ The Representative profile payload contains all base profile fields and Representative-specific fields.
+ Its structure is defined by the referenced Representative Profile Template.
+ """
+ }
+ signers: {
+ roles: {
+ user: [
+ "Representative",
+ ]
+ }
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/docs/representative_profile_template.cue b/specs/signed_docs/docs/representative_profile_template.cue
new file mode 100644
index 0000000000..aad79b365a
--- /dev/null
+++ b/specs/signed_docs/docs/representative_profile_template.cue
@@ -0,0 +1,42 @@
+@extern(embed)
+
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Representative Profile Template": {
+ description: "## Representative Profile Template\nDefines the JSON schema for a 'Representative Profile'.\nThis template allows an 'Admin' to enforce a specific structure and set of constraints for Representative profiles."
+ validation: """
+ * The signer MUST be a registered 'Admin'.
+ * The payload MUST be a valid JSON schema.
+ """
+ business_logic: {
+ front_end: """
+ """
+ back_end: """
+ * Validate and store the JSON schema that defines the structure for all 'Representative Profile' documents.
+ * The schema MUST extend the base 'Profile' schema with Representative-specific fields.
+ """
+ }
+ metadata: {
+ // Add any template-specific metadata here if needed
+ }
+ payload: {
+ description: """
+ JSON Schema document which defines the valid contents of a Representative profile document.
+ """
+ schema: _ @embed(file="payload_schemas/representative_profile_template.schema.json")
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/docs/voter_representative_delegation.cue b/specs/signed_docs/docs/voter_representative_delegation.cue
new file mode 100644
index 0000000000..34617c6b68
--- /dev/null
+++ b/specs/signed_docs/docs/voter_representative_delegation.cue
@@ -0,0 +1,61 @@
+@extern(embed)
+
+package signed_docs
+
+docs: #DocumentDefinitions & {
+ "Voter Representative Delegation": {
+ description: "## Voter Representative Delegation\nA signed document that allows a 'Voter' to delegate their voting power to a 'Representative' for a specific category."
+ validation: """
+ * The signer MUST be a registered 'Voter'.
+ * The 'ref' metadata field MUST point to a valid 'Representative Category Profile'.
+ * The payload MUST be empty.
+ """
+ business_logic: {
+ front_end: """
+ * Allows a voter to select a Representative from a list of eligible candidates for a category.
+ * The voter signs this document to confirm their delegation choice.
+ """
+ back_end: """
+ * Verifies that the voter and Representative are valid and registered for the category.
+ * Records the delegation of voting power from the voter to the Representative.
+ """
+ }
+ metadata: {
+ ref: {
+ required: "yes"
+ type: "Representative Category Profile"
+ }
+ parameters: {
+ required: "yes"
+ type: "Category Parameters"
+ }
+ }
+ payload: {
+ description: """
+ A minimal payload indicating the intended status of the delegation.
+ 'active' creates or affirms the delegation.
+ 'revoked' withdraws the delegation.
+ """
+ schema: _ @embed(file="payload_schemas/voter_representative_delegation.schema.json")
+ }
+ signers: {
+ roles: {
+ user: [
+ "Registered",
+ ]
+ }
+ }
+ authors: {
+ "Neil McAuliffe": "neil.mcauliffe@iohk.io"
+ }
+ versions: [
+ {
+ version: "0.01"
+ modified: "2025-06-19"
+ changes: """
+ * First Published Version
+ """
+ },
+ ]
+ }
+}
diff --git a/specs/signed_docs/documentation_links.cue b/specs/signed_docs/documentation_links.cue
index 4083dd2d34..ff9f1bc98d 100644
--- a/specs/signed_docs/documentation_links.cue
+++ b/specs/signed_docs/documentation_links.cue
@@ -24,15 +24,16 @@ import (
documentationLinks: #namedLink
documentationLinks: {
- "RFC3629": "https://datatracker.ietf.org/doc/html/rfc3629" // UTF-8
- "RFC3986": "https://datatracker.ietf.org/doc/html/rfc3986" // URI
- "RFC9562": "https://www.rfc-editor.org/rfc/rfc9562.html" // UUID
- "RFC9562-V4": "https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-4" // UUID V4
- "RFC9562-V7": "https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7" // UUID V7
- "CC-BY-4.0": "https://creativecommons.org/licenses/by/4.0/legalcode" // CC BY 4.0
- "IPFS-CID": "https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid" // IPFS Content Identifier
- "CBOR-TAG-42": "https://github.com/ipld/cid-cbor/"
- "CBOR-TAG-37": "https://github.com/lucas-clemente/cbor-specs/blob/master/uuid.md" // IPLD content identifiers (CIDs) in CBOR
+ "RFC3629": "https://datatracker.ietf.org/doc/html/rfc3629" // UTF-8
+ "RFC3986": "https://datatracker.ietf.org/doc/html/rfc3986" // URI
+ "RFC9562": "https://www.rfc-editor.org/rfc/rfc9562.html" // UUID
+ "RFC9562-V4": "https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-4" // UUID V4
+ "RFC9562-V7": "https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7" // UUID V7
+ "CC-BY-4.0": "https://creativecommons.org/licenses/by/4.0/legalcode" // CC BY 4.0
+ "IPFS-CID": "https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid" // IPFS Content Identifier
+ "CBOR-TAG-42": "https://github.com/ipld/cid-cbor/" // IPLD content identifiers (CIDs) in CBOR
+ "CBOR-TAG-37": "https://github.com/lucas-clemente/cbor-specs/blob/master/uuid.md" // UUID Tag for CBOR
+ "CBOR-LFD-ENCODING": "https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.3" // CBOR length-first core deterministic encoding requirements
}
#allLinkNames: or([
@@ -52,4 +53,5 @@ linkAKA: {
"UTF-8": "RFC3629"
"CBOR Encoded IPLD Content Identifier": "CBOR-TAG-42"
"IPFS CID": "IPFS-CID"
+ "CBOR Deterministic Encoding": "CBOR-LFD-ENCODING"
}
diff --git a/specs/signed_docs/payload.cue b/specs/signed_docs/payload.cue
index f2dcbec41c..b7e1a379df 100644
--- a/specs/signed_docs/payload.cue
+++ b/specs/signed_docs/payload.cue
@@ -1,5 +1,19 @@
package signed_docs
+import (
+ "list"
+)
+
+// Individual Payload Example
+#payloadExample: {
+ // Title of the example
+ title: string
+ // Expanded description of what the example shows.
+ description: string
+ // Example data that matches the payload schema.
+ example: _
+}
+
// Payload definition
_payload: {
// Description of the payload
@@ -7,4 +21,7 @@ _payload: {
// Optional fixed schema for the payload.
// A URI or inline JSON Schema that the payload must validate against.
schema?: _
+ // Examples of the schema.
+ examples?: list.UniqueItems
+ examples?: [...#payloadExample] | *[]
}