diff --git a/apps/portal-public/next.config.js b/apps/portal-public/next.config.js
index 53d8169..1991ec8 100644
--- a/apps/portal-public/next.config.js
+++ b/apps/portal-public/next.config.js
@@ -5,4 +5,3 @@ const nextConfig = {
};
module.exports = nextConfig;
-
diff --git a/apps/portal-public/package.json b/apps/portal-public/package.json
index 313622a..2455b38 100644
--- a/apps/portal-public/package.json
+++ b/apps/portal-public/package.json
@@ -7,7 +7,9 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
- "type-check": "tsc --noEmit"
+ "type-check": "tsc --noEmit",
+ "standards:generate": "tsx src/content/standards/generate-artifacts.ts",
+ "test": "vitest run"
},
"dependencies": {
"next": "^14.0.4",
@@ -26,20 +28,21 @@
"react-hook-form": "^7.49.2",
"@hookform/resolvers": "^3.3.4",
"zod": "^3.22.4",
- "date-fns": "^3.0.6",
- "zustand": "^4.4.7"
+ "date-fns": "^3.0.6"
},
"devDependencies": {
"@types/node": "^20.10.6",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"typescript": "^5.3.3",
+ "tsx": "^4.20.5",
"eslint": "^8.57.1",
"eslint-config-next": "^14.0.4",
"tailwindcss": "^3.4.1",
"postcss": "^8.4.33",
"autoprefixer": "^10.4.16",
- "tailwindcss-animate": "^1.0.7"
+ "baseline-browser-mapping": "^2.10.19",
+ "tailwindcss-animate": "^1.0.7",
+ "vitest": "^1.6.1"
}
}
-
diff --git a/apps/portal-public/public/standards/registry.json b/apps/portal-public/public/standards/registry.json
new file mode 100644
index 0000000..0d72125
--- /dev/null
+++ b/apps/portal-public/public/standards/registry.json
@@ -0,0 +1,699 @@
+{
+ "standards": [
+ {
+ "slug": "erc-3643",
+ "name": "ERC-3643",
+ "shortName": "Compliant Tokenization",
+ "eyebrow": "Compliant Token Standard",
+ "icon": "shield",
+ "summary": "Issue, manage, and distribute regulated digital assets with identity-based compliance enforced directly at the token level.",
+ "category": "Compliance and tokenization",
+ "status": "Live",
+ "audience": "Financial institutions, issuers, tokenization platforms, and regulated RWA teams.",
+ "standards": [
+ "ERC-3643",
+ "ERC-20"
+ ],
+ "publisher": "Tokeny",
+ "auditor": "Hacken",
+ "seo": {
+ "title": "ERC-3643 | The Order",
+ "description": "Compliant tokenization for real-world assets with identity-based compliance, lifecycle automation, and ERC-20 compatibility.",
+ "keywords": [
+ "ERC-3643",
+ "compliant tokenization",
+ "real-world assets",
+ "regulated digital assets"
+ ]
+ },
+ "specification": {
+ "label": "EIP-3643 Specification",
+ "href": "https://eips.ethereum.org/EIPS/eip-3643",
+ "kind": "spec"
+ },
+ "docs": [
+ {
+ "label": "Tokeny ERC-3643 Overview",
+ "href": "https://www.erc3643.org/",
+ "kind": "docs"
+ }
+ ],
+ "repos": [
+ {
+ "label": "ethereum/EIPs",
+ "href": "https://github.com/ethereum/EIPs",
+ "kind": "repo"
+ },
+ {
+ "label": "TokenySolutions/T-REX",
+ "href": "https://github.com/TokenySolutions/T-REX",
+ "kind": "repo"
+ }
+ ],
+ "submissionPoints": [
+ {
+ "label": "Ethereum EIPs Repository",
+ "href": "https://github.com/ethereum/EIPs",
+ "kind": "submission"
+ },
+ {
+ "label": "EIP-3643 Pull Requests",
+ "href": "https://github.com/ethereum/EIPs/pulls",
+ "kind": "submission"
+ }
+ ],
+ "compliance": {
+ "native": true,
+ "label": "Native compliance",
+ "summary": "Identity, eligibility, and transfer rules are enforced directly in token behavior rather than being bolted on externally.",
+ "enforcement": [
+ "Identity-aware transfer restrictions",
+ "Eligibility controls for holders and counterparties",
+ "Lifecycle rule updates during issuance and administration"
+ ]
+ },
+ "governanceBody": "Ethereum Improvement Proposal process with ecosystem implementation led by Tokeny and related adopters.",
+ "maintainers": [
+ {
+ "name": "Ethereum EIPs editors",
+ "role": "Canonical specification governance",
+ "href": "https://github.com/ethereum/EIPs"
+ },
+ {
+ "name": "Tokeny",
+ "role": "Reference implementation and ecosystem steward",
+ "href": "https://github.com/TokenySolutions/T-REX"
+ }
+ ],
+ "statusHistory": [
+ {
+ "date": "2021-11-09",
+ "title": "EIP-3643 created",
+ "summary": "The compliant tokenization standard was introduced through the Ethereum EIP process."
+ },
+ {
+ "date": "2022-01-26",
+ "title": "T-REX reference implementation publicized",
+ "summary": "Production-oriented implementation guidance and contracts became widely available through Tokeny."
+ },
+ {
+ "date": "2024-01-01",
+ "title": "Ecosystem adoption broadened",
+ "summary": "ERC-3643 continued to mature as a practical framework for regulated asset issuance and lifecycle management."
+ }
+ ],
+ "implementationExamples": [
+ {
+ "label": "Tokeny T-REX reference contracts",
+ "summary": "Production-oriented smart contract suite implementing identity-aware compliance controls for ERC-3643 assets.",
+ "href": "https://github.com/TokenySolutions/T-REX"
+ },
+ {
+ "label": "EIP-3643 canonical specification",
+ "summary": "The normative source for interface behavior, goals, and compliance-oriented design expectations.",
+ "href": "https://eips.ethereum.org/EIPS/eip-3643"
+ }
+ ],
+ "submissionChecklist": [
+ {
+ "label": "Canonical EIP specification reference",
+ "required": true,
+ "summary": "Point to the accepted EIP text and normative standard behavior."
+ },
+ {
+ "label": "Reference implementation repository",
+ "required": true,
+ "summary": "Include a public implementation repo that demonstrates how the standard is realized in production."
+ },
+ {
+ "label": "Compliance posture declaration",
+ "required": true,
+ "summary": "Document whether compliance is natively enforced or relies on external controls."
+ },
+ {
+ "label": "Submission points and governance path",
+ "required": true,
+ "summary": "Identify the repo, pull request surface, and governance venue for amendments or follow-on proposals."
+ }
+ ],
+ "ecosystemSupport": [
+ {
+ "area": "Wallet compatibility",
+ "status": "Moderate",
+ "summary": "Leverages ERC-20 compatibility, though full compliance-aware workflows depend on supporting infrastructure."
+ },
+ {
+ "area": "Exchange and marketplace support",
+ "status": "Specialized",
+ "summary": "Best fit for venues prepared to handle regulated asset onboarding and rule-aware transfer flows."
+ },
+ {
+ "area": "Issuer administration tooling",
+ "status": "Strong",
+ "summary": "Well suited to issuer-facing workflows, lifecycle events, and identity-driven operating models."
+ },
+ {
+ "area": "Regulated asset operations",
+ "status": "Strong",
+ "summary": "Purpose-built for permissioned or regulated issuance where transfer and holder eligibility matter."
+ }
+ ],
+ "comparisonTarget": "erc-20",
+ "featured": true,
+ "order": 10,
+ "href": "/standards/erc-3643",
+ "landingPage": {
+ "hero": {
+ "eyebrow": "Compliant Token Standard",
+ "headline": "Compliant Tokenization for Real-World Assets",
+ "subheadline": "ERC-3643 is the token standard for issuing, managing, and distributing regulated digital assets with identity-based compliance built into every transfer.",
+ "supportingCopy": "Designed for regulated markets, ERC-3643 combines token issuance, transfer control, lifecycle automation, and ecosystem compatibility in a single standard. It is built for teams bringing securities, funds, private debt, real estate, and other real-world assets onchain.",
+ "primaryCta": {
+ "label": "Explore ERC-3643",
+ "href": "/docs"
+ },
+ "secondaryCta": {
+ "label": "Compare Standards",
+ "href": "#comparison"
+ },
+ "tertiaryLink": {
+ "label": "View Documentation",
+ "href": "/docs"
+ }
+ },
+ "trustStrip": {
+ "label": "Standards and assurance",
+ "items": [
+ {
+ "label": "Standard",
+ "value": "ERC-3643, ERC-20 compatible"
+ },
+ {
+ "label": "Publisher",
+ "value": "Tokeny"
+ },
+ {
+ "label": "Auditor",
+ "value": "Hacken"
+ },
+ {
+ "label": "Category",
+ "value": "Compliant tokenization"
+ },
+ {
+ "label": "Fit",
+ "value": "Regulated digital assets and RWAs"
+ }
+ ]
+ },
+ "valueProps": {
+ "headline": "Built for compliant digital asset issuance",
+ "intro": "ERC-3643 embeds identity, eligibility, and transfer controls directly into the token so compliance is enforced as part of the asset itself, not added as a separate layer.",
+ "cards": [
+ {
+ "title": "Built-In Compliance",
+ "copy": "Ensure only eligible participants can hold and transfer assets through identity-based controls enforced onchain."
+ },
+ {
+ "title": "Lifecycle Automation",
+ "copy": "Support issuance, restrictions, corporate actions, and ongoing administration with extensive built-in functionality."
+ },
+ {
+ "title": "Regulated Asset Ready",
+ "copy": "Well suited for securities, funds, private debt, commodities, real estate, and other tokenized real-world assets."
+ },
+ {
+ "title": "ERC-20 Compatibility",
+ "copy": "Integrates with wallets, marketplaces, exchanges, and dApps while maintaining compliance enforcement."
+ },
+ {
+ "title": "Configurable Rules",
+ "copy": "Adapt compliance policies over time to meet changing operational, legal, or market requirements."
+ },
+ {
+ "title": "Proven Model",
+ "copy": "Validated across more than 120 use cases for compliant issuance and asset management."
+ }
+ ]
+ },
+ "useCases": {
+ "headline": "Designed for regulated and real-world asset use cases",
+ "intro": "ERC-3643 can be applied across a broad range of tokenized asset classes and identity-driven applications.",
+ "items": [
+ "Equities",
+ "Loans",
+ "Currencies",
+ "Funds and securities",
+ "Commodities",
+ "Private debt",
+ "Bonds",
+ "Real estate",
+ "Real-world assets",
+ "Identity",
+ "Loyalty"
+ ],
+ "supportingLine": "From institutional issuance to programmable asset distribution, ERC-3643 provides a consistent compliance framework across asset classes."
+ },
+ "proofPoints": {
+ "headline": "Trusted for compliant tokenization",
+ "intro": "ERC-3643 is designed for teams that need more than simple token issuance. It provides the operational and compliance foundation required for regulated digital asset programs.",
+ "items": [
+ "More than 120 validated use cases across compliant digital asset issuance and management.",
+ "Over 100 built-in functions for lifecycle operations, automation, and corporate actions.",
+ "Identity-based compliance enforced directly at the token level.",
+ "Broad compatibility with exchanges, marketplaces, wallets, and dApps."
+ ]
+ },
+ "comparison": {
+ "headline": "Why ERC-3643 instead of a basic token standard?",
+ "intro": "ERC-20 is foundational for fungible tokens, but it does not natively provide the compliance controls needed for regulated issuance. ERC-3643 extends ERC-20 compatibility with identity-aware transfer rules and operational controls built into the token.",
+ "columns": [
+ "Category",
+ "ERC-3643",
+ "ERC-20"
+ ],
+ "rows": [
+ {
+ "category": "Primary purpose",
+ "left": "Compliant tokenization for regulated assets",
+ "right": "General-purpose fungible tokens"
+ },
+ {
+ "category": "Compliance",
+ "left": "Enforced onchain through identity and rules",
+ "right": "Typically external or manual"
+ },
+ {
+ "category": "Transfer restrictions",
+ "left": "Native",
+ "right": "Not native"
+ },
+ {
+ "category": "Eligibility controls",
+ "left": "Native",
+ "right": "Not native"
+ },
+ {
+ "category": "Lifecycle management",
+ "left": "Extensive built-in functions",
+ "right": "Minimal core functions"
+ },
+ {
+ "category": "Best fit",
+ "left": "Regulated assets and RWAs",
+ "right": "Simple token issuance"
+ }
+ ],
+ "secondaryNote": "Compared with ERC-1400-style positioning, ERC-3643 is often easier to present as a practical, issuer-friendly standard for compliant tokenization with strong interoperability and lifecycle flexibility.",
+ "cta": {
+ "label": "View Full Comparison",
+ "href": "#comparison"
+ }
+ },
+ "faq": {
+ "headline": "Frequently asked questions",
+ "items": [
+ {
+ "question": "What is ERC-3643?",
+ "answer": "ERC-3643 is an Ethereum token standard for compliant tokenization. It combines ERC-20 compatibility with identity-based compliance controls that govern who can hold, transfer, and interact with tokenized assets."
+ },
+ {
+ "question": "How is ERC-3643 different from ERC-20?",
+ "answer": "ERC-20 defines the baseline behavior for fungible tokens, but it does not natively enforce compliance. ERC-3643 adds identity, eligibility, and transfer restrictions directly into token logic, making it better suited for regulated assets."
+ },
+ {
+ "question": "What types of assets can use ERC-3643?",
+ "answer": "ERC-3643 is well suited for equities, funds, securities, private debt, bonds, commodities, real estate, currencies, and other tokenized real-world assets."
+ },
+ {
+ "question": "Does ERC-3643 still work with the broader ecosystem?",
+ "answer": "Yes. ERC-3643 is designed to remain compatible with much of the broader Ethereum ecosystem, including wallets, dApps, marketplaces, and exchanges, while preserving compliance enforcement."
+ },
+ {
+ "question": "Who is ERC-3643 for?",
+ "answer": "It is particularly relevant for financial institutions, asset issuers, tokenization platforms, fund operators, and enterprises that need compliant digital asset issuance and lifecycle management."
+ }
+ ]
+ },
+ "footerCta": {
+ "headline": "Bring regulated assets onchain with greater control",
+ "copy": "Use ERC-3643 to issue compliant digital assets with identity-based transfer enforcement, lifecycle automation, and compatibility across the broader digital asset ecosystem.",
+ "primaryCta": {
+ "label": "Get Started",
+ "href": "/contact"
+ },
+ "secondaryCta": {
+ "label": "Talk to Sales",
+ "href": "/contact"
+ }
+ }
+ }
+ },
+ {
+ "slug": "erc-20",
+ "name": "ERC-20",
+ "shortName": "Fungible Token Baseline",
+ "eyebrow": "Foundational Token Standard",
+ "icon": "coins",
+ "summary": "The core fungible token standard for broad wallet, exchange, and application compatibility across the Ethereum ecosystem.",
+ "category": "General-purpose tokenization",
+ "status": "Live",
+ "audience": "Protocol teams, issuers, exchanges, treasury teams, and builders focused on interoperability.",
+ "standards": [
+ "ERC-20"
+ ],
+ "seo": {
+ "title": "ERC-20 | The Order",
+ "description": "The foundational fungible token standard for broad wallet, exchange, and application compatibility across Ethereum.",
+ "keywords": [
+ "ERC-20",
+ "fungible token",
+ "Ethereum token standard",
+ "token interoperability"
+ ]
+ },
+ "specification": {
+ "label": "EIP-20 Specification",
+ "href": "https://eips.ethereum.org/EIPS/eip-20",
+ "kind": "spec"
+ },
+ "docs": [
+ {
+ "label": "OpenZeppelin ERC-20 Guide",
+ "href": "https://docs.openzeppelin.com/contracts/4.x/erc20",
+ "kind": "docs"
+ }
+ ],
+ "repos": [
+ {
+ "label": "ethereum/EIPs",
+ "href": "https://github.com/ethereum/EIPs",
+ "kind": "repo"
+ },
+ {
+ "label": "OpenZeppelin/openzeppelin-contracts",
+ "href": "https://github.com/OpenZeppelin/openzeppelin-contracts",
+ "kind": "repo"
+ }
+ ],
+ "submissionPoints": [
+ {
+ "label": "Ethereum EIPs Repository",
+ "href": "https://github.com/ethereum/EIPs",
+ "kind": "submission"
+ },
+ {
+ "label": "EIP-20 Pull Requests",
+ "href": "https://github.com/ethereum/EIPs/pulls",
+ "kind": "submission"
+ }
+ ],
+ "compliance": {
+ "native": false,
+ "label": "External compliance required",
+ "summary": "ERC-20 does not natively enforce compliance. Regulated issuance typically depends on wrappers, policy layers, or additional contract systems.",
+ "enforcement": [
+ "External policy engines or offchain controls",
+ "Custom transfer restrictions when needed",
+ "Operational overlays for regulated counterparties"
+ ]
+ },
+ "governanceBody": "Ethereum Improvement Proposal process with widespread ecosystem stewardship across libraries, wallets, and exchanges.",
+ "maintainers": [
+ {
+ "name": "Ethereum EIPs editors",
+ "role": "Canonical specification governance",
+ "href": "https://github.com/ethereum/EIPs"
+ },
+ {
+ "name": "OpenZeppelin",
+ "role": "Widely used implementation maintainer",
+ "href": "https://github.com/OpenZeppelin/openzeppelin-contracts"
+ }
+ ],
+ "statusHistory": [
+ {
+ "date": "2015-11-19",
+ "title": "EIP-20 proposed",
+ "summary": "The fungible token interface was introduced as a standard for interoperable Ethereum assets."
+ },
+ {
+ "date": "2017-09-20",
+ "title": "EIP-20 finalized",
+ "summary": "ERC-20 became the canonical baseline for fungible token behavior across the ecosystem."
+ },
+ {
+ "date": "2024-01-01",
+ "title": "Baseline ecosystem standard maintained",
+ "summary": "ERC-20 remains the reference layer for fungible token interoperability and exchange integration."
+ }
+ ],
+ "implementationExamples": [
+ {
+ "label": "OpenZeppelin ERC-20 implementation",
+ "summary": "Widely used and audited Solidity implementation for fungible token behavior.",
+ "href": "https://github.com/OpenZeppelin/openzeppelin-contracts"
+ },
+ {
+ "label": "EIP-20 canonical specification",
+ "summary": "Normative interface definition for fungible token balances, transfers, and approvals.",
+ "href": "https://eips.ethereum.org/EIPS/eip-20"
+ }
+ ],
+ "submissionChecklist": [
+ {
+ "label": "Canonical EIP specification reference",
+ "required": true,
+ "summary": "Link the accepted EIP text as the normative basis for the registry entry."
+ },
+ {
+ "label": "Reference implementation repository",
+ "required": true,
+ "summary": "Provide at least one widely adopted implementation repo to anchor ecosystem usage."
+ },
+ {
+ "label": "Compliance dependency disclosure",
+ "required": true,
+ "summary": "State clearly that compliance is external if the token standard does not enforce it natively."
+ },
+ {
+ "label": "Submission and amendment path",
+ "required": true,
+ "summary": "Document the repository and PR path for future revisions to the standard."
+ }
+ ],
+ "ecosystemSupport": [
+ {
+ "area": "Wallet compatibility",
+ "status": "Strong",
+ "summary": "Near-universal support across Ethereum wallets and custody providers."
+ },
+ {
+ "area": "Exchange and marketplace support",
+ "status": "Strong",
+ "summary": "Deep support across centralized and decentralized trading venues."
+ },
+ {
+ "area": "Issuer administration tooling",
+ "status": "Moderate",
+ "summary": "Strong for basic issuance, though advanced compliance workflows usually require add-on systems."
+ },
+ {
+ "area": "Regulated asset operations",
+ "status": "Specialized",
+ "summary": "Can be used as a base layer, but regulated issuance typically needs additional policy and control infrastructure."
+ }
+ ],
+ "comparisonTarget": "erc-3643",
+ "featured": false,
+ "order": 20,
+ "href": "/standards/erc-20",
+ "landingPage": {
+ "hero": {
+ "eyebrow": "Foundational Token Standard",
+ "headline": "The Baseline Standard for Fungible Tokens",
+ "subheadline": "ERC-20 defines the core interface for fungible tokens on Ethereum and remains the foundation for interoperable token issuance across wallets, exchanges, and applications.",
+ "supportingCopy": "For teams that need broad ecosystem support and straightforward fungible token behavior, ERC-20 provides the common baseline. It is best suited to general-purpose token issuance and often serves as the interoperability layer beneath more specialized token frameworks.",
+ "primaryCta": {
+ "label": "Explore ERC-20",
+ "href": "/docs"
+ },
+ "secondaryCta": {
+ "label": "Compare Standards",
+ "href": "#comparison"
+ },
+ "tertiaryLink": {
+ "label": "View Documentation",
+ "href": "/docs"
+ }
+ },
+ "trustStrip": {
+ "label": "Standards overview",
+ "items": [
+ {
+ "label": "Standard",
+ "value": "ERC-20"
+ },
+ {
+ "label": "Role",
+ "value": "Baseline fungible token interface"
+ },
+ {
+ "label": "Category",
+ "value": "General-purpose tokenization"
+ },
+ {
+ "label": "Fit",
+ "value": "Interoperable token issuance and ecosystem compatibility"
+ },
+ {
+ "label": "Primary strength",
+ "value": "Simplicity and universal support"
+ }
+ ]
+ },
+ "valueProps": {
+ "headline": "Optimized for interoperability and simplicity",
+ "intro": "ERC-20 established the common language for fungible tokens across the Ethereum ecosystem. Its strength is broad adoption, predictable behavior, and straightforward integration.",
+ "cards": [
+ {
+ "title": "Universal Compatibility",
+ "copy": "Supported across wallets, exchanges, custodians, and dApps as the baseline fungible token format."
+ },
+ {
+ "title": "Simple Token Logic",
+ "copy": "Provides a minimal and predictable interface for balances, transfers, approvals, and allowances."
+ },
+ {
+ "title": "Builder Friendly",
+ "copy": "Well understood by developers, infrastructure providers, and integrators across the Ethereum ecosystem."
+ },
+ {
+ "title": "Strong Liquidity Fit",
+ "copy": "Works naturally with trading venues, pools, bridges, and DeFi protocols designed around fungible tokens."
+ },
+ {
+ "title": "Composable Foundation",
+ "copy": "Frequently used as the compatibility layer for more specialized token models and compliance overlays."
+ },
+ {
+ "title": "Fast to Adopt",
+ "copy": "A practical choice for teams that need broad support without embedding complex policy logic directly into the token."
+ }
+ ]
+ },
+ "useCases": {
+ "headline": "Best suited to general-purpose token issuance",
+ "intro": "ERC-20 is often the right choice when the goal is broad market compatibility rather than native compliance enforcement.",
+ "items": [
+ "Utility tokens",
+ "Stablecoins",
+ "Governance tokens",
+ "Rewards tokens",
+ "Exchange-listed assets",
+ "Protocol assets",
+ "Treasury instruments",
+ "Liquidity and settlement rails",
+ "Wrapped assets",
+ "Ecosystem incentives"
+ ],
+ "supportingLine": "ERC-20 can still be used in regulated contexts, but compliance controls generally need to be added through external systems, wrappers, or custom extensions."
+ },
+ "proofPoints": {
+ "headline": "Why teams still start with ERC-20",
+ "intro": "ERC-20 remains the default standard for fungible token interoperability because it balances simplicity, widespread support, and implementation maturity.",
+ "items": [
+ "Widely supported across the Ethereum ecosystem and token infrastructure stack.",
+ "Simple enough to implement, audit, and integrate without specialized compliance architecture.",
+ "Well suited to liquid, broadly distributed token models and exchange-facing assets.",
+ "Acts as the reference point for comparing more specialized token standards such as ERC-3643."
+ ]
+ },
+ "comparison": {
+ "headline": "When is ERC-20 enough, and when is it not?",
+ "intro": "ERC-20 is ideal when broad compatibility and simple fungible token behavior matter most. It becomes less suitable when regulated issuance requires native controls over identity, eligibility, and transfer permissions.",
+ "columns": [
+ "Category",
+ "ERC-20",
+ "ERC-3643"
+ ],
+ "rows": [
+ {
+ "category": "Primary purpose",
+ "left": "General-purpose fungible tokens",
+ "right": "Compliant tokenization for regulated assets"
+ },
+ {
+ "category": "Compliance model",
+ "left": "Usually external, manual, or custom",
+ "right": "Identity-based and enforced onchain"
+ },
+ {
+ "category": "Transfer restrictions",
+ "left": "Not native",
+ "right": "Native"
+ },
+ {
+ "category": "Eligibility controls",
+ "left": "Not native",
+ "right": "Native"
+ },
+ {
+ "category": "Best fit",
+ "left": "Broad ecosystem issuance and liquidity",
+ "right": "Regulated assets and RWAs"
+ },
+ {
+ "category": "Operational complexity",
+ "left": "Lower baseline complexity",
+ "right": "Higher capability with more built-in controls"
+ }
+ ],
+ "secondaryNote": "ERC-20 is the baseline standard. For regulated programs, it is often the starting point for comparison rather than the final architecture.",
+ "cta": {
+ "label": "View Standards Library",
+ "href": "/standards"
+ }
+ },
+ "faq": {
+ "headline": "Frequently asked questions",
+ "items": [
+ {
+ "question": "What is ERC-20?",
+ "answer": "ERC-20 is the standard interface for fungible tokens on Ethereum. It defines how balances, transfers, approvals, and allowances should behave so tokens can interoperate across the ecosystem."
+ },
+ {
+ "question": "Why is ERC-20 so widely used?",
+ "answer": "Its broad adoption comes from its simplicity and ecosystem support. Most wallets, exchanges, and DeFi applications are built to work with ERC-20 tokens by default."
+ },
+ {
+ "question": "Does ERC-20 include compliance controls?",
+ "answer": "Not natively. Teams that need regulated issuance or controlled transfers usually add compliance through external systems, custom extensions, or more specialized standards."
+ },
+ {
+ "question": "When should a team choose ERC-20?",
+ "answer": "ERC-20 is a strong choice when broad compatibility, liquidity integration, and straightforward token behavior matter more than embedding policy logic directly into the asset."
+ },
+ {
+ "question": "How does ERC-20 relate to ERC-3643?",
+ "answer": "ERC-3643 builds on ERC-20 compatibility while adding identity-based compliance and transfer controls. ERC-20 is the simpler baseline; ERC-3643 is the more compliance-oriented framework."
+ }
+ ]
+ },
+ "footerCta": {
+ "headline": "Start with the right token standard for the job",
+ "copy": "Use ERC-20 when you need broad compatibility and simple fungible token behavior, or compare it against specialized standards when compliance and lifecycle controls are part of the requirement.",
+ "primaryCta": {
+ "label": "Explore Standards",
+ "href": "/standards"
+ },
+ "secondaryCta": {
+ "label": "Talk to Sales",
+ "href": "/contact"
+ }
+ }
+ }
+ }
+ ],
+ "count": 2
+}
diff --git a/apps/portal-public/public/standards/schema.json b/apps/portal-public/public/standards/schema.json
new file mode 100644
index 0000000..26cc739
--- /dev/null
+++ b/apps/portal-public/public/standards/schema.json
@@ -0,0 +1,448 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://the-order.local/standards/schema.json",
+ "title": "The Order Standards Registry",
+ "description": "Canonical schema for standards registry entries used by the public portal.",
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/standardRegistryEntry"
+ },
+ "$defs": {
+ "standardIcon": {
+ "type": "string",
+ "enum": [
+ "shield",
+ "coins",
+ "scale",
+ "network",
+ "file-stack"
+ ]
+ },
+ "standardLink": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "label",
+ "href",
+ "kind"
+ ],
+ "properties": {
+ "label": {
+ "type": "string",
+ "minLength": 1
+ },
+ "href": {
+ "type": "string",
+ "format": "uri"
+ },
+ "kind": {
+ "type": "string",
+ "enum": [
+ "spec",
+ "repo",
+ "docs",
+ "submission"
+ ]
+ }
+ }
+ },
+ "seo": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "title",
+ "description"
+ ],
+ "properties": {
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1
+ },
+ "keywords": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "complianceProfile": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "native",
+ "label",
+ "summary",
+ "enforcement"
+ ],
+ "properties": {
+ "native": {
+ "type": "boolean"
+ },
+ "label": {
+ "type": "string",
+ "minLength": 1
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1
+ },
+ "enforcement": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "maintainer": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "name",
+ "role"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "role": {
+ "type": "string",
+ "minLength": 1
+ },
+ "href": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ },
+ "statusEvent": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "date",
+ "title",
+ "summary"
+ ],
+ "properties": {
+ "date": {
+ "type": "string",
+ "minLength": 1
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ },
+ "implementationExample": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "label",
+ "summary",
+ "href"
+ ],
+ "properties": {
+ "label": {
+ "type": "string",
+ "minLength": 1
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1
+ },
+ "href": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ },
+ "checklistItem": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "label",
+ "required",
+ "summary"
+ ],
+ "properties": {
+ "label": {
+ "type": "string",
+ "minLength": 1
+ },
+ "required": {
+ "type": "boolean"
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ },
+ "ecosystemSupport": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "area",
+ "status",
+ "summary"
+ ],
+ "properties": {
+ "area": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "Strong",
+ "Moderate",
+ "Specialized"
+ ]
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ },
+ "marketingLink": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "label",
+ "href"
+ ],
+ "properties": {
+ "label": {
+ "type": "string",
+ "minLength": 1
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ },
+ "standardRegistryEntry": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "slug",
+ "name",
+ "icon",
+ "summary",
+ "category",
+ "status",
+ "audience",
+ "standards",
+ "seo",
+ "specification",
+ "repos",
+ "submissionPoints",
+ "compliance",
+ "governanceBody",
+ "maintainers",
+ "statusHistory",
+ "implementationExamples",
+ "submissionChecklist",
+ "ecosystemSupport",
+ "order",
+ "href",
+ "landingPage"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "shortName": {
+ "type": "string",
+ "minLength": 1
+ },
+ "eyebrow": {
+ "type": "string",
+ "minLength": 1
+ },
+ "icon": {
+ "$ref": "#/$defs/standardIcon"
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "Live",
+ "Preview"
+ ]
+ },
+ "audience": {
+ "type": "string",
+ "minLength": 1
+ },
+ "standards": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "minLength": 1
+ }
+ },
+ "publisher": {
+ "type": "string",
+ "minLength": 1
+ },
+ "auditor": {
+ "type": "string",
+ "minLength": 1
+ },
+ "seo": {
+ "$ref": "#/$defs/seo"
+ },
+ "specification": {
+ "$ref": "#/$defs/standardLink"
+ },
+ "docs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/standardLink"
+ }
+ },
+ "repos": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/standardLink"
+ }
+ },
+ "submissionPoints": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/standardLink"
+ }
+ },
+ "compliance": {
+ "$ref": "#/$defs/complianceProfile"
+ },
+ "governanceBody": {
+ "type": "string",
+ "minLength": 1
+ },
+ "maintainers": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/maintainer"
+ }
+ },
+ "statusHistory": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/statusEvent"
+ }
+ },
+ "implementationExamples": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/implementationExample"
+ }
+ },
+ "submissionChecklist": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/checklistItem"
+ }
+ },
+ "ecosystemSupport": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/$defs/ecosystemSupport"
+ }
+ },
+ "comparisonTarget": {
+ "type": "string",
+ "minLength": 1
+ },
+ "featured": {
+ "type": "boolean"
+ },
+ "order": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1
+ },
+ "landingPage": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "hero"
+ ],
+ "properties": {
+ "hero": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "headline",
+ "subheadline"
+ ],
+ "properties": {
+ "eyebrow": {
+ "type": "string"
+ },
+ "headline": {
+ "type": "string"
+ },
+ "subheadline": {
+ "type": "string"
+ },
+ "supportingCopy": {
+ "type": "string"
+ },
+ "primaryCta": {
+ "$ref": "#/$defs/marketingLink"
+ },
+ "secondaryCta": {
+ "$ref": "#/$defs/marketingLink"
+ },
+ "tertiaryLink": {
+ "$ref": "#/$defs/marketingLink"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/portal-public/src/app/about/page.tsx b/apps/portal-public/src/app/about/page.tsx
index 36cbc83..83adac6 100644
--- a/apps/portal-public/src/app/about/page.tsx
+++ b/apps/portal-public/src/app/about/page.tsx
@@ -1,4 +1,4 @@
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import { Card, CardContent, CardHeader, CardTitle } from '../../lib/ui-server';
export default function AboutPage(): JSX.Element {
return (
@@ -39,7 +39,7 @@ export default function AboutPage(): JSX.Element {
•
eResidency: Digital residency credentials for individuals seeking
- to participate in The Order's ecosystem
+ to participate in The Order's ecosystem
@@ -113,4 +113,3 @@ export default function AboutPage(): JSX.Element {
);
}
-
diff --git a/apps/portal-public/src/app/api/standards/[slug]/route.ts b/apps/portal-public/src/app/api/standards/[slug]/route.ts
new file mode 100644
index 0000000..7a06376
--- /dev/null
+++ b/apps/portal-public/src/app/api/standards/[slug]/route.ts
@@ -0,0 +1,15 @@
+import { NextResponse } from 'next/server';
+import { getStandardBySlug } from '../../../../content/standards';
+
+export async function GET(
+ _request: Request,
+ { params }: { params: { slug: string } }
+): Promise {
+ const standard = getStandardBySlug(params.slug);
+
+ if (!standard) {
+ return NextResponse.json({ error: 'Standard not found' }, { status: 404 });
+ }
+
+ return NextResponse.json(standard);
+}
diff --git a/apps/portal-public/src/app/api/standards/route.ts b/apps/portal-public/src/app/api/standards/route.ts
new file mode 100644
index 0000000..12abcab
--- /dev/null
+++ b/apps/portal-public/src/app/api/standards/route.ts
@@ -0,0 +1,35 @@
+import { NextResponse } from 'next/server';
+import { getSortedStandards } from '../../../content/standards';
+import { filterStandards } from '../../../content/standards/filter';
+
+export async function GET(request: Request): Promise {
+ const { searchParams } = new URL(request.url);
+ const query = searchParams.get('q')?.trim().toLowerCase() ?? '';
+ const category = searchParams.get('category')?.trim().toLowerCase() ?? '';
+ const status = searchParams.get('status')?.trim().toLowerCase() ?? '';
+ const featured = searchParams.get('featured');
+ const compliance = searchParams.get('compliance')?.trim().toLowerCase() ?? '';
+ const limit = Number(searchParams.get('limit') ?? '');
+
+ const standards = filterStandards(getSortedStandards(), {
+ q: query,
+ category,
+ status,
+ featured,
+ compliance,
+ limit: Number.isFinite(limit) && limit > 0 ? limit : null,
+ });
+
+ return NextResponse.json({
+ standards,
+ count: standards.length,
+ filters: {
+ q: query || null,
+ category: category || null,
+ status: status || null,
+ featured: featured ?? null,
+ compliance: compliance || null,
+ limit: Number.isFinite(limit) && limit > 0 ? limit : null,
+ },
+ });
+}
diff --git a/apps/portal-public/src/app/api/standards/schema/route.ts b/apps/portal-public/src/app/api/standards/schema/route.ts
new file mode 100644
index 0000000..e068bc7
--- /dev/null
+++ b/apps/portal-public/src/app/api/standards/schema/route.ts
@@ -0,0 +1,6 @@
+import { NextResponse } from 'next/server';
+import { standardsRegistryJsonSchema } from '../../../../content/standards/schema-json';
+
+export async function GET(): Promise {
+ return NextResponse.json(standardsRegistryJsonSchema);
+}
diff --git a/apps/portal-public/src/app/contact/page.tsx b/apps/portal-public/src/app/contact/page.tsx
index 8cbb6a5..5396743 100644
--- a/apps/portal-public/src/app/contact/page.tsx
+++ b/apps/portal-public/src/app/contact/page.tsx
@@ -22,7 +22,7 @@ export default function ContactPage(): JSX.Element {
await new Promise((resolve) => setTimeout(resolve, 1000));
success('Message sent successfully!', 'We will get back to you soon.');
setFormData({ name: '', email: '', subject: '', message: '' });
- } catch (err) {
+ } catch {
showError('Failed to send message', 'Please try again later.');
} finally {
setIsSubmitting(false);
@@ -35,14 +35,14 @@ export default function ContactPage(): JSX.Element {
Contact Us
- Have questions? We'd love to hear from you.
+ Have questions? We'd love to hear from you.
Send us a message
- Fill out the form below and we'll get back to you as soon as possible.
+ Fill out the form below and we'll get back to you as soon as possible.
);
}
-
diff --git a/apps/portal-public/src/app/not-found.tsx b/apps/portal-public/src/app/not-found.tsx
index 5f8f227..ad86f97 100644
--- a/apps/portal-public/src/app/not-found.tsx
+++ b/apps/portal-public/src/app/not-found.tsx
@@ -1,5 +1,5 @@
import Link from 'next/link';
-import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../lib/ui-server';
export default function NotFound(): JSX.Element {
return (
@@ -11,19 +11,18 @@ export default function NotFound(): JSX.Element {
- The page you're looking for doesn't exist or has been moved.
+ The page you're looking for doesn't exist or has been moved.
Go Home
- window.history.back()}>
- Go Back
-
+
+ View Docs
+
);
}
-
diff --git a/apps/portal-public/src/app/page.tsx b/apps/portal-public/src/app/page.tsx
index b1bcdd3..7175362 100644
--- a/apps/portal-public/src/app/page.tsx
+++ b/apps/portal-public/src/app/page.tsx
@@ -1,21 +1,49 @@
import Link from 'next/link';
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../lib/ui-server';
+import { getComparisonTarget, getFeaturedStandards, getSortedStandards } from '../content/standards';
+import { StandardIcon } from '../components/marketing/standard-icon';
export default function Home(): JSX.Element {
+ const featuredStandard = getFeaturedStandards()[0] ?? getSortedStandards()[0];
+ const standardsPreview = getSortedStandards().slice(0, 2);
+
return (
-
+
-
-
The Order
-
- Order of Military Hospitallers - Digital Identity & Governance Platform
-
-
- Apply for eResidency, verify credentials, and access our services
-
+
+
+
+ Public Portal
+
+
The Order
+
+ Order of Military Hospitallers digital identity, standards, and governance platform.
+
+
+ Apply for eResidency, verify credentials, review implementation standards, and access
+ public documentation for regulated digital asset programs.
+
+
+
+ Begin Application
+
+
+ Explore Standards
+
+
+
-
+
Apply for eResidency
@@ -88,6 +116,151 @@ export default function Home(): JSX.Element {
+
+ {featuredStandard ? (
+
+
+
+
+ Featured standard
+
+
+ Standards built for regulated digital asset programs
+
+
+
+ Browse all standards
+
+
+
+
+
+
+
+ {featuredStandard.status}
+
+
+ {featuredStandard.compliance.native ? 'Native compliance' : 'External compliance'}
+
+
+
+
+
+ {featuredStandard.category}
+
+
+
+ {featuredStandard.name}
+
+ {featuredStandard.summary}
+
+
+
+
+
+
+
Audience
+
{featuredStandard.audience}
+
+
+
Standards
+
+ {featuredStandard.standards.join(', ')}
+
+
+
+
+
+ {featuredStandard.publisher ?
Publisher: {featuredStandard.publisher}
: null}
+ {featuredStandard.auditor ?
Auditor: {featuredStandard.auditor}
: null}
+ {getComparisonTarget(featuredStandard) ? (
+
Compare with: {getComparisonTarget(featuredStandard)?.name}
+ ) : null}
+
Submission points: {featuredStandard.submissionPoints.length}
+
+
+ View {featuredStandard.name}
+
+
+
+
+
+ ) : null}
+
+ {standardsPreview.length > 0 ? (
+
+
+ {standardsPreview.map((standard) => (
+
+
+
+
+ {standard.status}
+
+ {standard.featured ? (
+ Featured
+ ) : null}
+
+ {standard.compliance.native ? 'Native compliance' : 'External compliance'}
+
+
+
+
+
+
+
+ {standard.name}
+
+
+ {standard.summary}
+
+
+
+
+
+
{standard.audience}
+
+ Submission points: {standard.submissionPoints.length}
+
+ {getComparisonTarget(standard) ? (
+
+ Compare with {getComparisonTarget(standard)?.name}
+
+ ) : null}
+
+
+ View standard
+
+
+
+ ))}
+
+
+ ) : null}
);
diff --git a/apps/portal-public/src/app/privacy/page.tsx b/apps/portal-public/src/app/privacy/page.tsx
index e4e5b84..88ba6fe 100644
--- a/apps/portal-public/src/app/privacy/page.tsx
+++ b/apps/portal-public/src/app/privacy/page.tsx
@@ -1,4 +1,4 @@
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import { Card, CardContent, CardHeader, CardTitle } from '../../lib/ui-server';
export default function PrivacyPage(): JSX.Element {
return (
@@ -91,4 +91,3 @@ export default function PrivacyPage(): JSX.Element {
);
}
-
diff --git a/apps/portal-public/src/app/standards/[slug]/page.tsx b/apps/portal-public/src/app/standards/[slug]/page.tsx
new file mode 100644
index 0000000..79549b1
--- /dev/null
+++ b/apps/portal-public/src/app/standards/[slug]/page.tsx
@@ -0,0 +1,57 @@
+import type { Metadata } from 'next';
+import { notFound } from 'next/navigation';
+import { MarketingLandingPage } from '../../../components/marketing/landing-page';
+import { getRelatedStandards, getSortedStandards, getStandardBySlug } from '../../../content/standards';
+import { StandardRegistryDetails } from '../../../components/marketing/standard-registry-details';
+import { RelatedStandards } from '../../../components/marketing/related-standards';
+
+export function generateStaticParams(): { slug: string }[] {
+ return getSortedStandards().map((standard) => ({ slug: standard.slug }));
+}
+
+export function generateMetadata({
+ params,
+}: {
+ params: { slug: string };
+}): Metadata {
+ const standard = getStandardBySlug(params.slug);
+
+ if (!standard) {
+ return {
+ title: 'Standard Not Found | The Order',
+ };
+ }
+
+ return {
+ title: standard.seo.title,
+ description: standard.seo.description,
+ keywords: standard.seo.keywords,
+ openGraph: {
+ title: standard.seo.title,
+ description: standard.seo.description,
+ url: standard.href,
+ },
+ };
+}
+
+export default function StandardDetailPage({
+ params,
+}: {
+ params: { slug: string };
+}): JSX.Element {
+ const standard = getStandardBySlug(params.slug);
+
+ if (!standard) {
+ notFound();
+ }
+
+ const relatedStandards = getRelatedStandards(standard);
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
diff --git a/apps/portal-public/src/app/standards/admin/page.tsx b/apps/portal-public/src/app/standards/admin/page.tsx
new file mode 100644
index 0000000..f87bcb5
--- /dev/null
+++ b/apps/portal-public/src/app/standards/admin/page.tsx
@@ -0,0 +1,11 @@
+import type { Metadata } from 'next';
+import { StandardsAdmin } from '../../../components/marketing/standards-admin';
+
+export const metadata: Metadata = {
+ title: 'Standards Admin | The Order',
+ description: 'Authoring and validation interface for standards registry entries.',
+};
+
+export default function StandardsAdminPage(): JSX.Element {
+ return
;
+}
diff --git a/apps/portal-public/src/app/standards/page.tsx b/apps/portal-public/src/app/standards/page.tsx
new file mode 100644
index 0000000..af8b410
--- /dev/null
+++ b/apps/portal-public/src/app/standards/page.tsx
@@ -0,0 +1,13 @@
+import type { Metadata } from 'next';
+import { StandardsBrowser } from '../../components/marketing/standards-browser';
+import { getSortedStandards } from '../../content/standards';
+
+export const metadata: Metadata = {
+ title: 'Standards | The Order',
+ description:
+ 'Explore standards and frameworks for compliant tokenization, regulated digital asset issuance, and real-world asset programs.',
+};
+
+export default function StandardsPage(): JSX.Element {
+ return
;
+}
diff --git a/apps/portal-public/src/app/status/page.tsx b/apps/portal-public/src/app/status/page.tsx
index 34c13fb..79c8835 100644
--- a/apps/portal-public/src/app/status/page.tsx
+++ b/apps/portal-public/src/app/status/page.tsx
@@ -1,11 +1,32 @@
'use client';
+import { Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { useQuery } from '@tanstack/react-query';
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Label } from '@the-order/ui';
import { getApiClient } from '@the-order/api-client';
-export default function StatusPage(): JSX.Element {
+function StatusCardShell({ children }: { children: React.ReactNode }): JSX.Element {
+ return (
+
+ );
+}
+
+function StatusLoadingState(): JSX.Element {
+ return (
+
+
+
+ Loading application status...
+
+
+
+ );
+}
+
+function StatusPageContent(): JSX.Element {
const searchParams = useSearchParams();
const applicationId = searchParams.get('id');
const apiClient = getApiClient();
@@ -21,63 +42,47 @@ export default function StatusPage(): JSX.Element {
if (!applicationId) {
return (
-
-
-
-
- Application Status
- Please provide an application ID to check status
-
-
-
-
+
+
+
+ Application Status
+ Please provide an application ID to check status
+
+
+
);
}
if (isLoading) {
- return (
-
-
-
-
- Loading application status...
-
-
-
-
- );
+ return
;
}
if (error) {
return (
-
-
-
-
- Error
- Failed to load application status
-
-
- {error instanceof Error ? error.message : 'Unknown error'}
-
-
-
-
+
+
+
+ Error
+ Failed to load application status
+
+
+ {error instanceof Error ? error.message : 'Unknown error'}
+
+
+
);
}
if (!application) {
return (
-
-
-
-
- Application Not Found
- No application found with the provided ID
-
-
-
-
+
+
+
+ Application Not Found
+ No application found with the provided ID
+
+
+
);
}
@@ -97,8 +102,7 @@ export default function StatusPage(): JSX.Element {
};
return (
-
-
+
Application Status
@@ -163,9 +167,15 @@ export default function StatusPage(): JSX.Element {
)}
-
-
+
);
}
+export default function StatusPage(): JSX.Element {
+ return (
+
}>
+
+
+ );
+}
diff --git a/apps/portal-public/src/app/terms/page.tsx b/apps/portal-public/src/app/terms/page.tsx
index 17823c9..e1f80f8 100644
--- a/apps/portal-public/src/app/terms/page.tsx
+++ b/apps/portal-public/src/app/terms/page.tsx
@@ -1,4 +1,4 @@
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import { Card, CardContent, CardHeader, CardTitle } from '../../lib/ui-server';
export default function TermsPage(): JSX.Element {
return (
@@ -16,7 +16,7 @@ export default function TermsPage(): JSX.Element {
- By accessing and using The Order's services, you accept and agree to be bound by
+ By accessing and using The Order's services, you accept and agree to be bound by
these Terms of Service. If you do not agree, you may not use our services.
@@ -99,4 +99,3 @@ export default function TermsPage(): JSX.Element {
);
}
-
diff --git a/apps/portal-public/src/components/AuthGuard.tsx b/apps/portal-public/src/components/AuthGuard.tsx
index a674620..0da8413 100644
--- a/apps/portal-public/src/components/AuthGuard.tsx
+++ b/apps/portal-public/src/components/AuthGuard.tsx
@@ -4,9 +4,19 @@ import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../lib/auth';
-export function AuthGuard({ children }: { children: React.ReactNode }): JSX.Element | null {
- const { isAuthenticated, isLoading } = useAuth();
+export function AuthGuard({
+ children,
+ requiredRoles,
+}: {
+ children: React.ReactNode;
+ requiredRoles?: string[];
+}): JSX.Element | null {
+ const { isAuthenticated, isLoading, user } = useAuth();
const router = useRouter();
+ const hasRequiredRole =
+ !requiredRoles || requiredRoles.length === 0
+ ? true
+ : requiredRoles.some((role) => user?.roles?.includes(role));
useEffect(() => {
if (!isLoading && !isAuthenticated) {
@@ -14,6 +24,12 @@ export function AuthGuard({ children }: { children: React.ReactNode }): JSX.Elem
}
}, [isAuthenticated, isLoading, router]);
+ useEffect(() => {
+ if (!isLoading && isAuthenticated && !hasRequiredRole) {
+ router.push('/');
+ }
+ }, [hasRequiredRole, isAuthenticated, isLoading, router]);
+
if (isLoading) {
return (
@@ -26,6 +42,9 @@ export function AuthGuard({ children }: { children: React.ReactNode }): JSX.Elem
return null;
}
+ if (!hasRequiredRole) {
+ return null;
+ }
+
return <>{children}>;
}
-
diff --git a/apps/portal-public/src/components/Footer.tsx b/apps/portal-public/src/components/Footer.tsx
index f81fc51..dac0f48 100644
--- a/apps/portal-public/src/components/Footer.tsx
+++ b/apps/portal-public/src/components/Footer.tsx
@@ -39,6 +39,11 @@ export function Footer(): JSX.Element {
About
+
+
+ Standards
+
+
Documentation
@@ -74,4 +79,3 @@ export function Footer(): JSX.Element {
);
}
-
diff --git a/apps/portal-public/src/components/Header.tsx b/apps/portal-public/src/components/Header.tsx
index 74fbe7b..33ee7d0 100644
--- a/apps/portal-public/src/components/Header.tsx
+++ b/apps/portal-public/src/components/Header.tsx
@@ -8,6 +8,7 @@ import { useAuth } from '../lib/auth';
export function Header(): JSX.Element {
const router = useRouter();
const { isAuthenticated, user, logout } = useAuth();
+ const canAccessStandardsAdmin = !!user?.roles?.some((role) => role === 'admin' || role === 'editor');
const handleLogout = (): void => {
logout();
@@ -25,6 +26,9 @@ export function Header(): JSX.Element {
Apply
+
+ Standards
+
Status
@@ -36,6 +40,11 @@ export function Header(): JSX.Element {
{isAuthenticated ? (
+ {canAccessStandardsAdmin ? (
+
+ Registry Admin
+
+ ) : null}
{user?.email || user?.name}
Logout
diff --git a/apps/portal-public/src/components/marketing/landing-page.tsx b/apps/portal-public/src/components/marketing/landing-page.tsx
new file mode 100644
index 0000000..4b1e94b
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/landing-page.tsx
@@ -0,0 +1,450 @@
+import type { ReactNode } from 'react';
+import Link from 'next/link';
+import {
+ Badge,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+ cn,
+} from '../../lib/ui-server';
+
+export interface MarketingLink {
+ label: string;
+ href: string;
+}
+
+export interface MarketingHero {
+ eyebrow?: string;
+ headline: string;
+ subheadline: string;
+ supportingCopy?: string;
+ primaryCta?: MarketingLink;
+ secondaryCta?: MarketingLink;
+ tertiaryLink?: MarketingLink;
+}
+
+export interface MarketingTrustItem {
+ label: string;
+ value: string;
+}
+
+export interface MarketingTrustStrip {
+ label?: string;
+ items: MarketingTrustItem[];
+}
+
+export interface MarketingCardItem {
+ title: string;
+ copy: string;
+}
+
+export interface MarketingValueProps {
+ headline: string;
+ intro?: string;
+ cards: MarketingCardItem[];
+}
+
+export interface MarketingUseCases {
+ headline: string;
+ intro?: string;
+ items: string[];
+ supportingLine?: string;
+}
+
+export interface MarketingComparisonRow {
+ category: string;
+ left: string;
+ right: string;
+}
+
+export interface MarketingComparison {
+ headline: string;
+ intro?: string;
+ columns: [string, string, string];
+ rows: MarketingComparisonRow[];
+ secondaryNote?: string;
+ cta?: MarketingLink;
+}
+
+export interface MarketingProofPoints {
+ headline: string;
+ intro?: string;
+ items: string[];
+}
+
+export interface MarketingFaqItem {
+ question: string;
+ answer: string;
+}
+
+export interface MarketingFaq {
+ headline: string;
+ items: MarketingFaqItem[];
+}
+
+export interface MarketingFooterCta {
+ headline: string;
+ copy: string;
+ primaryCta?: MarketingLink;
+ secondaryCta?: MarketingLink;
+}
+
+export interface MarketingLandingPageProps {
+ hero: MarketingHero;
+ trustStrip?: MarketingTrustStrip;
+ valueProps?: MarketingValueProps;
+ useCases?: MarketingUseCases;
+ proofPoints?: MarketingProofPoints;
+ comparison?: MarketingComparison;
+ faq?: MarketingFaq;
+ footerCta?: MarketingFooterCta;
+}
+
+function SectionShell({
+ id,
+ className,
+ children,
+}: {
+ id?: string;
+ className?: string;
+ children: ReactNode;
+}): JSX.Element {
+ return (
+
+ );
+}
+
+function SectionIntro({
+ headline,
+ intro,
+ align = 'left',
+ tone = 'default',
+}: {
+ headline: string;
+ intro?: string;
+ align?: 'left' | 'center';
+ tone?: 'default' | 'inverse';
+}): JSX.Element {
+ return (
+
+
+ {headline}
+
+ {intro ? (
+
+ {intro}
+
+ ) : null}
+
+ );
+}
+
+function CtaLink({
+ href,
+ label,
+ variant = 'primary',
+}: MarketingLink & {
+ variant?: 'primary' | 'secondary' | 'link';
+}): JSX.Element {
+ return (
+
+ {label}
+
+ );
+}
+
+export function MarketingHeroSection({
+ eyebrow,
+ headline,
+ subheadline,
+ supportingCopy,
+ primaryCta,
+ secondaryCta,
+ tertiaryLink,
+}: MarketingHero): JSX.Element {
+ return (
+
+
+
+
+
+ {eyebrow ? (
+
+ {eyebrow}
+
+ ) : null}
+
+ {headline}
+
+
+ {subheadline}
+
+ {supportingCopy ? (
+
+ {supportingCopy}
+
+ ) : null}
+
+ {primaryCta ? : null}
+ {secondaryCta ? : null}
+ {tertiaryLink ? : null}
+
+
+
+
+
+
+ Operational focus
+
+ Designed for regulated issuance
+
+
+
+
+
120+
+
Validated use cases
+
+
+
100+
+
Built-in functions
+
+
+
+ Combine identity-aware transfer rules, lifecycle automation, and ERC-20 compatibility in one
+ implementation-ready standard.
+
+
+
+
+
+
+ );
+}
+
+export function MarketingTrustStripSection({ label, items }: MarketingTrustStrip): JSX.Element {
+ return (
+
+
+
+ {label ? (
+
+ ) : null}
+
+ {items.map((item) => (
+
+
{item.label}
+ {item.value}
+
+ ))}
+
+
+
+
+ );
+}
+
+export function MarketingValueGridSection({ headline, intro, cards }: MarketingValueProps): JSX.Element {
+ return (
+
+
+
+ {cards.map((card) => (
+
+
+ {card.title}
+
+
+ {card.copy}
+
+
+ ))}
+
+
+ );
+}
+
+export function MarketingUseCasesSection({ headline, intro, items, supportingLine }: MarketingUseCases): JSX.Element {
+ return (
+
+
+
+ {items.map((item) => (
+
+ {item}
+
+ ))}
+
+ {supportingLine ? {supportingLine}
: null}
+
+ );
+}
+
+export function MarketingProofPointsSection({ headline, intro, items }: MarketingProofPoints): JSX.Element {
+ return (
+
+
+
+
+ {items.map((item, index) => (
+
+
+
+ {index + 1}
+
+ {item}
+
+
+ ))}
+
+
+
+ );
+}
+
+export function MarketingComparisonSection({
+ headline,
+ intro,
+ columns,
+ rows,
+ secondaryNote,
+ cta,
+}: MarketingComparison): JSX.Element {
+ return (
+
+
+
+
+
+
+ {columns[0]}
+ {columns[1]}
+ {columns[2]}
+
+
+
+ {rows.map((row) => (
+
+ {row.category}
+ {row.left}
+ {row.right}
+
+ ))}
+
+
+
+ {secondaryNote ? {secondaryNote}
: null}
+ {cta ? (
+
+
+
+ ) : null}
+
+ );
+}
+
+export function MarketingFaqSection({ headline, items }: MarketingFaq): JSX.Element {
+ return (
+
+
+
+ {items.map((item) => (
+
+
+ {item.question}
+ +
+
+ {item.answer}
+
+ ))}
+
+
+ );
+}
+
+export function MarketingFooterCtaSection({
+ headline,
+ copy,
+ primaryCta,
+ secondaryCta,
+}: MarketingFooterCta): JSX.Element {
+ return (
+
+
+
+
{headline}
+
{copy}
+
+ {primaryCta ? : null}
+ {secondaryCta ? : null}
+
+
+
+
+ );
+}
+
+export function MarketingLandingPage({
+ hero,
+ trustStrip,
+ valueProps,
+ useCases,
+ proofPoints,
+ comparison,
+ faq,
+ footerCta,
+}: MarketingLandingPageProps): JSX.Element {
+ return (
+
+
+ {trustStrip ? : null}
+ {valueProps ? : null}
+ {useCases ? : null}
+ {proofPoints ? : null}
+ {comparison ? : null}
+ {faq ? : null}
+ {footerCta ? : null}
+
+ );
+}
diff --git a/apps/portal-public/src/components/marketing/related-standards.tsx b/apps/portal-public/src/components/marketing/related-standards.tsx
new file mode 100644
index 0000000..1dbb8e7
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/related-standards.tsx
@@ -0,0 +1,62 @@
+import Link from 'next/link';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
+import type { StandardRegistryEntry } from '../../content/standards';
+import { StandardIcon } from './standard-icon';
+
+export function RelatedStandards({
+ standards,
+}: {
+ standards: StandardRegistryEntry[];
+}): JSX.Element | null {
+ if (standards.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+
Related standards
+
+ Compare adjacent standards in the registry
+
+
+ These standards are related by explicit comparison targets, shared categories, or overlapping standard families.
+
+
+
+ {standards.map((standard) => (
+
+
+
+
+
+
+ {standard.name}
+
+ {standard.shortName ?? standard.category}
+
+
+ {standard.summary}
+
+
+ View standard
+
+
+ Jump to comparison
+
+
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/apps/portal-public/src/components/marketing/standard-icon.tsx b/apps/portal-public/src/components/marketing/standard-icon.tsx
new file mode 100644
index 0000000..4204235
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/standard-icon.tsx
@@ -0,0 +1,25 @@
+import { Coins, FileStack, Network, Scale, ShieldCheck } from 'lucide-react';
+import type { StandardIconName } from '../../content/standards/schema';
+
+const iconClassName = 'h-5 w-5';
+
+export function StandardIcon({
+ name,
+ className = iconClassName,
+}: {
+ name: StandardIconName;
+ className?: string;
+}): JSX.Element {
+ switch (name) {
+ case 'shield':
+ return ;
+ case 'coins':
+ return ;
+ case 'scale':
+ return ;
+ case 'network':
+ return ;
+ case 'file-stack':
+ return ;
+ }
+}
diff --git a/apps/portal-public/src/components/marketing/standard-registry-details.tsx b/apps/portal-public/src/components/marketing/standard-registry-details.tsx
new file mode 100644
index 0000000..940c3d9
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/standard-registry-details.tsx
@@ -0,0 +1,266 @@
+import Link from 'next/link';
+import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
+import type { StandardRegistryEntry } from '../../content/standards';
+import { StandardIcon } from './standard-icon';
+
+function ExternalLinkList({
+ title,
+ links,
+}: {
+ title: string;
+ links: StandardRegistryEntry['repos'] | StandardRegistryEntry['submissionPoints'] | StandardRegistryEntry['docs'];
+}): JSX.Element | null {
+ if (!links || links.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+ {title}
+
+
+ {links.map((link) => (
+
+ {link.label}
+ {link.href}
+
+ ))}
+
+
+ );
+}
+
+export function StandardRegistryDetails({
+ standard,
+}: {
+ standard: StandardRegistryEntry;
+}): JSX.Element {
+ return (
+
+
+
+
+
Registry details
+
+ Canonical metadata, compliance posture, and submission points
+
+
+ This registry entry is designed to be implementation-friendly: canonical spec links,
+ repository references, and the official submission points are captured directly in content.
+
+
+
+ Back to standards
+
+
+
+
+
+
+
+
+
+ {standard.status}
+
+
+ {standard.compliance.label}
+
+
+
+
+
+
+ {standard.name}
+
+ {standard.summary}
+
+
+
+
Category
+
{standard.category}
+
+
+
Audience
+
{standard.audience}
+
+
+
+
Submission coverage
+
+ {standard.submissionPoints.length} submission point{standard.submissionPoints.length === 1 ? '' : 's'} tracked
+
+
+
+
+
+
+
+ Compliance profile
+
+ {standard.compliance.summary}
+
+
+
+
+ {standard.compliance.enforcement.map((item) => (
+
+ {item}
+
+ ))}
+
+
+
+
+
+
+ Governance and maintainers
+
+ {standard.governanceBody}
+
+
+
+ {standard.maintainers.map((maintainer) => (
+
+
{maintainer.name}
+
{maintainer.role}
+ {maintainer.href ? (
+
+ View maintainer
+
+ ) : null}
+
+ ))}
+
+
+
+
+
+ Status history
+
+ Important milestones that help explain how the standard evolved and where it sits today.
+
+
+
+ {standard.statusHistory.map((event) => (
+
+
{event.date}
+
{event.title}
+
{event.summary}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ Implementation examples
+
+
+ {standard.implementationExamples.map((example) => (
+
+ {example.label}
+ {example.summary}
+
+ ))}
+
+
+
+
+
+ Submission checklist
+
+ Minimum elements tracked in the registry to keep the entry complete and submission-ready.
+
+
+
+ {standard.submissionChecklist.map((item) => (
+
+
+
+ {item.required ? 'Required' : 'Optional'}
+
+
{item.label}
+
+
{item.summary}
+
+ ))}
+
+
+
+
+
+ Ecosystem support
+
+ Snapshot of how the standard tends to perform across core infrastructure and operating contexts.
+
+
+
+ {standard.ecosystemSupport.map((item) => (
+
+
+
{item.area}
+
+ {item.status}
+
+
+
{item.summary}
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/apps/portal-public/src/components/marketing/standards-admin.tsx b/apps/portal-public/src/components/marketing/standards-admin.tsx
new file mode 100644
index 0000000..b5a3f33
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/standards-admin.tsx
@@ -0,0 +1,223 @@
+'use client';
+
+import { useEffect, useMemo, useState } from 'react';
+import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import { standardsRegistry, type StandardRegistryEntry } from '../../content/standards';
+import { standardRegistryEntrySchema } from '../../content/standards/schema';
+import { standardsRegistryTemplate } from '../../content/standards/template';
+import { AuthGuard } from '../AuthGuard';
+
+function prettyPrint(value: unknown): string {
+ return `${JSON.stringify(value, null, 2)}\n`;
+}
+
+const DRAFT_STORAGE_KEY = 'standards-admin-draft';
+const SELECTION_STORAGE_KEY = 'standards-admin-selection';
+
+export function StandardsAdmin(): JSX.Element {
+ const [selection, setSelection] = useState('template');
+ const [draft, setDraft] = useState(prettyPrint(standardsRegistryTemplate));
+ const [copyState, setCopyState] = useState<'idle' | 'copied'>('idle');
+
+ const sourceEntries: Record = useMemo(() => {
+ const entries = Object.fromEntries(standardsRegistry.map((standard) => [standard.slug, standard]));
+ return {
+ template: standardsRegistryTemplate,
+ ...entries,
+ };
+ }, []);
+
+ const validation = useMemo(() => {
+ try {
+ const parsedJson = JSON.parse(draft);
+ const parsedStandard = standardRegistryEntrySchema.safeParse(parsedJson);
+
+ if (!parsedStandard.success) {
+ return {
+ valid: false,
+ message: parsedStandard.error.issues[0]?.message ?? 'Validation failed.',
+ parsed: null as StandardRegistryEntry | null,
+ };
+ }
+
+ return {
+ valid: true,
+ message: 'Entry is valid against the standards registry schema.',
+ parsed: parsedStandard.data as StandardRegistryEntry,
+ };
+ } catch (error) {
+ return {
+ valid: false,
+ message: error instanceof Error ? error.message : 'Invalid JSON.',
+ parsed: null as StandardRegistryEntry | null,
+ };
+ }
+ }, [draft]);
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+ const storedSelection = localStorage.getItem(SELECTION_STORAGE_KEY);
+ const storedDraft = localStorage.getItem(DRAFT_STORAGE_KEY);
+
+ if (storedSelection && sourceEntries[storedSelection]) {
+ setSelection(storedSelection);
+ }
+
+ if (storedDraft) {
+ setDraft(storedDraft);
+ } else if (storedSelection && sourceEntries[storedSelection]) {
+ setDraft(prettyPrint(sourceEntries[storedSelection]));
+ }
+ }, [sourceEntries]);
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+ localStorage.setItem(DRAFT_STORAGE_KEY, draft);
+ }, [draft]);
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+ localStorage.setItem(SELECTION_STORAGE_KEY, selection);
+ }, [selection]);
+
+ const handleCopy = async (): Promise => {
+ await navigator.clipboard.writeText(draft);
+ setCopyState('copied');
+ window.setTimeout(() => setCopyState('idle'), 1500);
+ };
+
+ const handleExport = (): void => {
+ const blob = new Blob([draft], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const anchor = document.createElement('a');
+ anchor.href = url;
+ anchor.download = `${selection || 'standard-entry'}.json`;
+ anchor.click();
+ URL.revokeObjectURL(url);
+ };
+
+ const handleReset = (): void => {
+ const nextDraft = prettyPrint(sourceEntries[selection] ?? standardsRegistryTemplate);
+ setDraft(nextDraft);
+ };
+
+ return (
+
+
+
+
+
+ Registry Authoring
+
+
+ Standards registry editor
+
+
+ Use this authoring surface to start from the template or an existing standard, edit raw JSON,
+ validate it against the registry schema, and prepare a submission-ready entry.
+
+
+
+
+
+
+ Source entry
+
+ Load the template or an existing standard into the editor.
+
+
+
+ {
+ const nextSelection = event.target.value;
+ setSelection(nextSelection);
+ setDraft(prettyPrint(sourceEntries[nextSelection]));
+ }}
+ >
+ Template
+ {standardsRegistry.map((standard) => (
+
+ {standard.name}
+
+ ))}
+
+
+
+ Validation status:
+
+
+ {validation.valid ? 'Valid' : 'Invalid'}
+
+
+
{validation.message}
+
+
+ {validation.parsed ? (
+
+
Slug: {validation.parsed.slug}
+
Href: {validation.parsed.href}
+
Compliance: {validation.parsed.compliance.label}
+
Submission points: {validation.parsed.submissionPoints.length}
+
+ ) : null}
+
+
+
+
+
+ JSON editor
+
+ Edit the raw registry entry. This surface validates structure, required fields, and normalized enums.
+
+
+
+
+ {
+ void handleCopy();
+ }}
+ className="rounded-full bg-slate-950 px-4 py-2 text-sm font-semibold text-white"
+ >
+ {copyState === 'copied' ? 'Copied' : 'Copy JSON'}
+
+
+ Export JSON
+
+
+ Reset draft
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/portal-public/src/components/marketing/standards-browser.tsx b/apps/portal-public/src/components/marketing/standards-browser.tsx
new file mode 100644
index 0000000..5813b84
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/standards-browser.tsx
@@ -0,0 +1,309 @@
+'use client';
+
+import { useState } from 'react';
+import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
+import type { StandardRegistryEntry } from '../../content/standards';
+import { StandardIcon } from './standard-icon';
+import Link from 'next/link';
+import { getComparisonTarget } from '../../content/standards';
+
+export function StandardsBrowser({
+ standards,
+}: {
+ standards: StandardRegistryEntry[];
+}): JSX.Element {
+ const [query, setQuery] = useState('');
+ const [complianceFilter, setComplianceFilter] = useState<'all' | 'native' | 'external'>('all');
+ const [statusFilter, setStatusFilter] = useState<'all' | 'live' | 'preview'>('all');
+ const [featuredFilter, setFeaturedFilter] = useState<'all' | 'featured' | 'non-featured'>('all');
+ const [categoryFilter, setCategoryFilter] = useState('all');
+ const [viewMode, setViewMode] = useState<'list' | 'table'>('list');
+
+ const normalizedQuery = query.trim().toLowerCase();
+ const categories = Array.from(new Set(standards.map((standard) => standard.category))).sort();
+ const filteredStandards = standards.filter((standard) => {
+ const matchesQuery =
+ normalizedQuery.length === 0 ||
+ standard.name.toLowerCase().includes(normalizedQuery) ||
+ (standard.shortName?.toLowerCase().includes(normalizedQuery) ?? false) ||
+ standard.summary.toLowerCase().includes(normalizedQuery) ||
+ standard.category.toLowerCase().includes(normalizedQuery) ||
+ standard.standards.some((item) => item.toLowerCase().includes(normalizedQuery));
+
+ const matchesCompliance =
+ complianceFilter === 'all' ||
+ (complianceFilter === 'native' && standard.compliance.native) ||
+ (complianceFilter === 'external' && !standard.compliance.native);
+
+ const matchesStatus =
+ statusFilter === 'all' ||
+ (statusFilter === 'live' && standard.status === 'Live') ||
+ (statusFilter === 'preview' && standard.status === 'Preview');
+
+ const matchesFeatured =
+ featuredFilter === 'all' ||
+ (featuredFilter === 'featured' && !!standard.featured) ||
+ (featuredFilter === 'non-featured' && !standard.featured);
+
+ const matchesCategory = categoryFilter === 'all' || standard.category === categoryFilter;
+
+ return matchesQuery && matchesCompliance && matchesStatus && matchesFeatured && matchesCategory;
+ });
+
+ return (
+
+
+
+
+
+ Search and filter standards
+
+ Narrow the registry by keyword or by whether compliance is native to the standard.
+
+
+
+
+ Search
+ setQuery(event.target.value)}
+ placeholder="Search by name, category, standard, or summary"
+ />
+
+
+ Compliance mode
+
+ setComplianceFilter(event.target.value as 'all' | 'native' | 'external')
+ }
+ >
+ All standards
+ Native compliance
+ External compliance
+
+
+
+ Status
+ setStatusFilter(event.target.value as 'all' | 'live' | 'preview')}
+ >
+ All statuses
+ Live
+ Preview
+
+
+
+ Category
+ setCategoryFilter(event.target.value)}
+ >
+ All categories
+ {categories.map((category) => (
+
+ {category}
+
+ ))}
+
+
+
+ Featured
+
+ setFeaturedFilter(event.target.value as 'all' | 'featured' | 'non-featured')
+ }
+ >
+ All entries
+ Featured only
+ Non-featured only
+
+
+
+
+
+ {filteredStandards.length} result{filteredStandards.length === 1 ? '' : 's'}
+
+
+ setViewMode('list')}
+ className={`rounded-full px-3 py-1 text-sm font-medium ${
+ viewMode === 'list' ? 'bg-slate-950 text-white' : 'bg-slate-100 text-slate-700'
+ }`}
+ >
+ List
+
+ setViewMode('table')}
+ className={`rounded-full px-3 py-1 text-sm font-medium ${
+ viewMode === 'table' ? 'bg-slate-950 text-white' : 'bg-slate-100 text-slate-700'
+ }`}
+ >
+ Table
+
+
+ {query ? (
+ setQuery('')}
+ className="text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
+ >
+ Clear search
+
+ ) : null}
+
+
+
+
+
+ {filteredStandards.length > 0 ? (
+ viewMode === 'list' ? (
+
+
+ {filteredStandards.map((standard) => {
+ const comparisonTarget = getComparisonTarget(standard);
+ return (
+
+
+
+
+ {standard.status}
+
+
+ {standard.compliance.native ? 'Native compliance' : 'External compliance'}
+
+
+
+
+
+
+
+ {standard.name}
+
+
+ {standard.shortName ?? standard.summary}
+
+
+
+
+ {standard.summary}
+
+
+ View standard
+
+ {comparisonTarget ? (
+
+ Compare with {comparisonTarget.name}
+
+ ) : null}
+
+
+
+ );
+ })}
+
+
+ ) : (
+
+
+
+
+
+
+ Standard
+ Category
+ Compliance
+ Status
+ Submission points
+ Actions
+
+
+
+ {filteredStandards.map((standard) => {
+ const comparisonTarget = getComparisonTarget(standard);
+ return (
+
+
+
+
+
+
+
+
{standard.name}
+
{standard.shortName ?? standard.summary}
+
+
+
+ {standard.category}
+ {standard.compliance.label}
+ {standard.status}
+ {standard.submissionPoints.length}
+
+
+
+ View
+
+ {comparisonTarget ? (
+
+ Compare
+
+ ) : null}
+
+
+
+ );
+ })}
+
+
+
+
+
+ )
+ ) : (
+
+
+
+
+
+
+
+ No standards matched
+
+ Try a broader keyword or switch the compliance filter back to show all standards in the registry.
+
+
+
+
+
+ )}
+
+ );
+}
diff --git a/apps/portal-public/src/components/marketing/standards-index.tsx b/apps/portal-public/src/components/marketing/standards-index.tsx
new file mode 100644
index 0000000..71bed34
--- /dev/null
+++ b/apps/portal-public/src/components/marketing/standards-index.tsx
@@ -0,0 +1,138 @@
+import Link from 'next/link';
+import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
+import { getComparisonTarget, type StandardRegistryEntry } from '../../content/standards';
+import { StandardIcon } from './standard-icon';
+
+export function StandardsIndexPage({
+ standards,
+ compactHero = false,
+}: {
+ standards: StandardRegistryEntry[];
+ compactHero?: boolean;
+}): JSX.Element {
+ return (
+
+
+
+ {!compactHero ? (
+
+
+ Standards Library
+
+
+ Standards and frameworks for regulated digital asset programs
+
+
+ Explore token standards, compliance frameworks, and implementation-ready guidance for
+ digital asset issuance across regulated and real-world asset use cases.
+
+
+ ) : null}
+
+
+ {standards.map((standard) => {
+ const comparisonTarget = getComparisonTarget(standard);
+
+ return (
+
+
+
+
+ {standard.status}
+
+ {standard.featured ? (
+ Featured
+ ) : null}
+
+ {standard.compliance.native ? 'Native compliance' : 'External compliance'}
+
+
+ {standard.category}
+
+
+
+
+
+
+
+ {standard.name}
+
+ {standard.shortName ? (
+
+ {standard.shortName}
+
+ ) : null}
+
+
+
+ {standard.summary}
+
+
+
Audience
+ {standard.audience}
+
+
+
Standards
+
+ {standard.standards.join(', ')}
+
+
+
+
Specification
+ {standard.specification.label}
+
+
+
Submission points
+ {standard.submissionPoints.length}
+
+
+
+ {standard.publisher ? Publisher: {standard.publisher} : null}
+ {standard.auditor ? Auditor: {standard.auditor} : null}
+
+
+
+ View standard
+
+
+ Open spec
+
+ {comparisonTarget ? (
+
+ Compare with {comparisonTarget.name}
+
+ ) : null}
+
+
+
+ );
+ })}
+
+
+
+
+ );
+}
diff --git a/apps/portal-public/src/content/erc20.ts b/apps/portal-public/src/content/erc20.ts
new file mode 100644
index 0000000..e73e483
--- /dev/null
+++ b/apps/portal-public/src/content/erc20.ts
@@ -0,0 +1,203 @@
+import type { MarketingLandingPageProps } from '../components/marketing/landing-page';
+
+export const erc20LandingPage: MarketingLandingPageProps = {
+ hero: {
+ eyebrow: 'Foundational Token Standard',
+ headline: 'The Baseline Standard for Fungible Tokens',
+ subheadline:
+ 'ERC-20 defines the core interface for fungible tokens on Ethereum and remains the foundation for interoperable token issuance across wallets, exchanges, and applications.',
+ supportingCopy:
+ 'For teams that need broad ecosystem support and straightforward fungible token behavior, ERC-20 provides the common baseline. It is best suited to general-purpose token issuance and often serves as the interoperability layer beneath more specialized token frameworks.',
+ primaryCta: {
+ label: 'Explore ERC-20',
+ href: '/docs',
+ },
+ secondaryCta: {
+ label: 'Compare Standards',
+ href: '#comparison',
+ },
+ tertiaryLink: {
+ label: 'View Documentation',
+ href: '/docs',
+ },
+ },
+ trustStrip: {
+ label: 'Standards overview',
+ items: [
+ {
+ label: 'Standard',
+ value: 'ERC-20',
+ },
+ {
+ label: 'Role',
+ value: 'Baseline fungible token interface',
+ },
+ {
+ label: 'Category',
+ value: 'General-purpose tokenization',
+ },
+ {
+ label: 'Fit',
+ value: 'Interoperable token issuance and ecosystem compatibility',
+ },
+ {
+ label: 'Primary strength',
+ value: 'Simplicity and universal support',
+ },
+ ],
+ },
+ valueProps: {
+ headline: 'Optimized for interoperability and simplicity',
+ intro:
+ 'ERC-20 established the common language for fungible tokens across the Ethereum ecosystem. Its strength is broad adoption, predictable behavior, and straightforward integration.',
+ cards: [
+ {
+ title: 'Universal Compatibility',
+ copy:
+ 'Supported across wallets, exchanges, custodians, and dApps as the baseline fungible token format.',
+ },
+ {
+ title: 'Simple Token Logic',
+ copy:
+ 'Provides a minimal and predictable interface for balances, transfers, approvals, and allowances.',
+ },
+ {
+ title: 'Builder Friendly',
+ copy:
+ 'Well understood by developers, infrastructure providers, and integrators across the Ethereum ecosystem.',
+ },
+ {
+ title: 'Strong Liquidity Fit',
+ copy:
+ 'Works naturally with trading venues, pools, bridges, and DeFi protocols designed around fungible tokens.',
+ },
+ {
+ title: 'Composable Foundation',
+ copy:
+ 'Frequently used as the compatibility layer for more specialized token models and compliance overlays.',
+ },
+ {
+ title: 'Fast to Adopt',
+ copy:
+ 'A practical choice for teams that need broad support without embedding complex policy logic directly into the token.',
+ },
+ ],
+ },
+ useCases: {
+ headline: 'Best suited to general-purpose token issuance',
+ intro:
+ 'ERC-20 is often the right choice when the goal is broad market compatibility rather than native compliance enforcement.',
+ items: [
+ 'Utility tokens',
+ 'Stablecoins',
+ 'Governance tokens',
+ 'Rewards tokens',
+ 'Exchange-listed assets',
+ 'Protocol assets',
+ 'Treasury instruments',
+ 'Liquidity and settlement rails',
+ 'Wrapped assets',
+ 'Ecosystem incentives',
+ ],
+ supportingLine:
+ 'ERC-20 can still be used in regulated contexts, but compliance controls generally need to be added through external systems, wrappers, or custom extensions.',
+ },
+ proofPoints: {
+ headline: 'Why teams still start with ERC-20',
+ intro:
+ 'ERC-20 remains the default standard for fungible token interoperability because it balances simplicity, widespread support, and implementation maturity.',
+ items: [
+ 'Widely supported across the Ethereum ecosystem and token infrastructure stack.',
+ 'Simple enough to implement, audit, and integrate without specialized compliance architecture.',
+ 'Well suited to liquid, broadly distributed token models and exchange-facing assets.',
+ 'Acts as the reference point for comparing more specialized token standards such as ERC-3643.',
+ ],
+ },
+ comparison: {
+ headline: 'When is ERC-20 enough, and when is it not?',
+ intro:
+ 'ERC-20 is ideal when broad compatibility and simple fungible token behavior matter most. It becomes less suitable when regulated issuance requires native controls over identity, eligibility, and transfer permissions.',
+ columns: ['Category', 'ERC-20', 'ERC-3643'],
+ rows: [
+ {
+ category: 'Primary purpose',
+ left: 'General-purpose fungible tokens',
+ right: 'Compliant tokenization for regulated assets',
+ },
+ {
+ category: 'Compliance model',
+ left: 'Usually external, manual, or custom',
+ right: 'Identity-based and enforced onchain',
+ },
+ {
+ category: 'Transfer restrictions',
+ left: 'Not native',
+ right: 'Native',
+ },
+ {
+ category: 'Eligibility controls',
+ left: 'Not native',
+ right: 'Native',
+ },
+ {
+ category: 'Best fit',
+ left: 'Broad ecosystem issuance and liquidity',
+ right: 'Regulated assets and RWAs',
+ },
+ {
+ category: 'Operational complexity',
+ left: 'Lower baseline complexity',
+ right: 'Higher capability with more built-in controls',
+ },
+ ],
+ secondaryNote:
+ 'ERC-20 is the baseline standard. For regulated programs, it is often the starting point for comparison rather than the final architecture.',
+ cta: {
+ label: 'View Standards Library',
+ href: '/standards',
+ },
+ },
+ faq: {
+ headline: 'Frequently asked questions',
+ items: [
+ {
+ question: 'What is ERC-20?',
+ answer:
+ 'ERC-20 is the standard interface for fungible tokens on Ethereum. It defines how balances, transfers, approvals, and allowances should behave so tokens can interoperate across the ecosystem.',
+ },
+ {
+ question: 'Why is ERC-20 so widely used?',
+ answer:
+ 'Its broad adoption comes from its simplicity and ecosystem support. Most wallets, exchanges, and DeFi applications are built to work with ERC-20 tokens by default.',
+ },
+ {
+ question: 'Does ERC-20 include compliance controls?',
+ answer:
+ 'Not natively. Teams that need regulated issuance or controlled transfers usually add compliance through external systems, custom extensions, or more specialized standards.',
+ },
+ {
+ question: 'When should a team choose ERC-20?',
+ answer:
+ 'ERC-20 is a strong choice when broad compatibility, liquidity integration, and straightforward token behavior matter more than embedding policy logic directly into the asset.',
+ },
+ {
+ question: 'How does ERC-20 relate to ERC-3643?',
+ answer:
+ 'ERC-3643 builds on ERC-20 compatibility while adding identity-based compliance and transfer controls. ERC-20 is the simpler baseline; ERC-3643 is the more compliance-oriented framework.',
+ },
+ ],
+ },
+ footerCta: {
+ headline: 'Start with the right token standard for the job',
+ copy:
+ 'Use ERC-20 when you need broad compatibility and simple fungible token behavior, or compare it against specialized standards when compliance and lifecycle controls are part of the requirement.',
+ primaryCta: {
+ label: 'Explore Standards',
+ href: '/standards',
+ },
+ secondaryCta: {
+ label: 'Talk to Sales',
+ href: '/contact',
+ },
+ },
+};
diff --git a/apps/portal-public/src/content/erc3643.ts b/apps/portal-public/src/content/erc3643.ts
new file mode 100644
index 0000000..a219d23
--- /dev/null
+++ b/apps/portal-public/src/content/erc3643.ts
@@ -0,0 +1,203 @@
+import type { MarketingLandingPageProps } from '../components/marketing/landing-page';
+
+export const erc3643LandingPage: MarketingLandingPageProps = {
+ hero: {
+ eyebrow: 'Compliant Token Standard',
+ headline: 'Compliant Tokenization for Real-World Assets',
+ subheadline:
+ 'ERC-3643 is the token standard for issuing, managing, and distributing regulated digital assets with identity-based compliance built into every transfer.',
+ supportingCopy:
+ 'Designed for regulated markets, ERC-3643 combines token issuance, transfer control, lifecycle automation, and ecosystem compatibility in a single standard. It is built for teams bringing securities, funds, private debt, real estate, and other real-world assets onchain.',
+ primaryCta: {
+ label: 'Explore ERC-3643',
+ href: '/docs',
+ },
+ secondaryCta: {
+ label: 'Compare Standards',
+ href: '#comparison',
+ },
+ tertiaryLink: {
+ label: 'View Documentation',
+ href: '/docs',
+ },
+ },
+ trustStrip: {
+ label: 'Standards and assurance',
+ items: [
+ {
+ label: 'Standard',
+ value: 'ERC-3643, ERC-20 compatible',
+ },
+ {
+ label: 'Publisher',
+ value: 'Tokeny',
+ },
+ {
+ label: 'Auditor',
+ value: 'Hacken',
+ },
+ {
+ label: 'Category',
+ value: 'Compliant tokenization',
+ },
+ {
+ label: 'Fit',
+ value: 'Regulated digital assets and RWAs',
+ },
+ ],
+ },
+ valueProps: {
+ headline: 'Built for compliant digital asset issuance',
+ intro:
+ 'ERC-3643 embeds identity, eligibility, and transfer controls directly into the token so compliance is enforced as part of the asset itself, not added as a separate layer.',
+ cards: [
+ {
+ title: 'Built-In Compliance',
+ copy:
+ 'Ensure only eligible participants can hold and transfer assets through identity-based controls enforced onchain.',
+ },
+ {
+ title: 'Lifecycle Automation',
+ copy:
+ 'Support issuance, restrictions, corporate actions, and ongoing administration with extensive built-in functionality.',
+ },
+ {
+ title: 'Regulated Asset Ready',
+ copy:
+ 'Well suited for securities, funds, private debt, commodities, real estate, and other tokenized real-world assets.',
+ },
+ {
+ title: 'ERC-20 Compatibility',
+ copy:
+ 'Integrates with wallets, marketplaces, exchanges, and dApps while maintaining compliance enforcement.',
+ },
+ {
+ title: 'Configurable Rules',
+ copy:
+ 'Adapt compliance policies over time to meet changing operational, legal, or market requirements.',
+ },
+ {
+ title: 'Proven Model',
+ copy: 'Validated across more than 120 use cases for compliant issuance and asset management.',
+ },
+ ],
+ },
+ useCases: {
+ headline: 'Designed for regulated and real-world asset use cases',
+ intro:
+ 'ERC-3643 can be applied across a broad range of tokenized asset classes and identity-driven applications.',
+ items: [
+ 'Equities',
+ 'Loans',
+ 'Currencies',
+ 'Funds and securities',
+ 'Commodities',
+ 'Private debt',
+ 'Bonds',
+ 'Real estate',
+ 'Real-world assets',
+ 'Identity',
+ 'Loyalty',
+ ],
+ supportingLine:
+ 'From institutional issuance to programmable asset distribution, ERC-3643 provides a consistent compliance framework across asset classes.',
+ },
+ proofPoints: {
+ headline: 'Trusted for compliant tokenization',
+ intro:
+ 'ERC-3643 is designed for teams that need more than simple token issuance. It provides the operational and compliance foundation required for regulated digital asset programs.',
+ items: [
+ 'More than 120 validated use cases across compliant digital asset issuance and management.',
+ 'Over 100 built-in functions for lifecycle operations, automation, and corporate actions.',
+ 'Identity-based compliance enforced directly at the token level.',
+ 'Broad compatibility with exchanges, marketplaces, wallets, and dApps.',
+ ],
+ },
+ comparison: {
+ headline: 'Why ERC-3643 instead of a basic token standard?',
+ intro:
+ 'ERC-20 is foundational for fungible tokens, but it does not natively provide the compliance controls needed for regulated issuance. ERC-3643 extends ERC-20 compatibility with identity-aware transfer rules and operational controls built into the token.',
+ columns: ['Category', 'ERC-3643', 'ERC-20'],
+ rows: [
+ {
+ category: 'Primary purpose',
+ left: 'Compliant tokenization for regulated assets',
+ right: 'General-purpose fungible tokens',
+ },
+ {
+ category: 'Compliance',
+ left: 'Enforced onchain through identity and rules',
+ right: 'Typically external or manual',
+ },
+ {
+ category: 'Transfer restrictions',
+ left: 'Native',
+ right: 'Not native',
+ },
+ {
+ category: 'Eligibility controls',
+ left: 'Native',
+ right: 'Not native',
+ },
+ {
+ category: 'Lifecycle management',
+ left: 'Extensive built-in functions',
+ right: 'Minimal core functions',
+ },
+ {
+ category: 'Best fit',
+ left: 'Regulated assets and RWAs',
+ right: 'Simple token issuance',
+ },
+ ],
+ secondaryNote:
+ 'Compared with ERC-1400-style positioning, ERC-3643 is often easier to present as a practical, issuer-friendly standard for compliant tokenization with strong interoperability and lifecycle flexibility.',
+ cta: {
+ label: 'View Full Comparison',
+ href: '#comparison',
+ },
+ },
+ faq: {
+ headline: 'Frequently asked questions',
+ items: [
+ {
+ question: 'What is ERC-3643?',
+ answer:
+ 'ERC-3643 is an Ethereum token standard for compliant tokenization. It combines ERC-20 compatibility with identity-based compliance controls that govern who can hold, transfer, and interact with tokenized assets.',
+ },
+ {
+ question: 'How is ERC-3643 different from ERC-20?',
+ answer:
+ 'ERC-20 defines the baseline behavior for fungible tokens, but it does not natively enforce compliance. ERC-3643 adds identity, eligibility, and transfer restrictions directly into token logic, making it better suited for regulated assets.',
+ },
+ {
+ question: 'What types of assets can use ERC-3643?',
+ answer:
+ 'ERC-3643 is well suited for equities, funds, securities, private debt, bonds, commodities, real estate, currencies, and other tokenized real-world assets.',
+ },
+ {
+ question: 'Does ERC-3643 still work with the broader ecosystem?',
+ answer:
+ 'Yes. ERC-3643 is designed to remain compatible with much of the broader Ethereum ecosystem, including wallets, dApps, marketplaces, and exchanges, while preserving compliance enforcement.',
+ },
+ {
+ question: 'Who is ERC-3643 for?',
+ answer:
+ 'It is particularly relevant for financial institutions, asset issuers, tokenization platforms, fund operators, and enterprises that need compliant digital asset issuance and lifecycle management.',
+ },
+ ],
+ },
+ footerCta: {
+ headline: 'Bring regulated assets onchain with greater control',
+ copy:
+ 'Use ERC-3643 to issue compliant digital assets with identity-based transfer enforcement, lifecycle automation, and compatibility across the broader digital asset ecosystem.',
+ primaryCta: {
+ label: 'Get Started',
+ href: '/contact',
+ },
+ secondaryCta: {
+ label: 'Talk to Sales',
+ href: '/contact',
+ },
+ },
+};
diff --git a/apps/portal-public/src/content/standards/README.md b/apps/portal-public/src/content/standards/README.md
new file mode 100644
index 0000000..0567324
--- /dev/null
+++ b/apps/portal-public/src/content/standards/README.md
@@ -0,0 +1,66 @@
+# Standards Registry Authoring Guide
+
+This registry is the canonical content system for standards pages in the public portal.
+
+## What a complete entry must include
+
+Each standard entry should be complete enough to power:
+
+- the homepage featured/preview cards
+- the `/standards` browser and filters
+- the detail page
+- the API export routes
+- future search, integrations, or content syndication
+
+Minimum required content:
+
+- canonical specification link
+- at least one repository reference
+- at least one submission point
+- compliance posture
+- governance and maintainer information
+- status history
+- implementation example
+- submission checklist
+- ecosystem support notes
+
+## How to add a new standard
+
+1. Copy the shape from [template.ts](./template.ts).
+2. Create or import the marketing landing page content file.
+3. Fill in all registry metadata fields.
+4. Add the entry to [index.ts](./index.ts).
+5. Confirm the `href`, `slug`, and `comparisonTarget` values line up.
+6. Run `pnpm --dir the-order/apps/portal-public type-check`.
+
+## Native compliance policy
+
+Use `compliance.native = true` only when the standard itself natively enforces compliance or transfer eligibility at the token or protocol layer.
+
+Use `compliance.native = false` when compliance depends on:
+
+- wrappers
+- external policy engines
+- offchain operating controls
+- custom overlays beyond the standard itself
+
+## Submission points
+
+Every entry should identify where updates or official proposals are submitted.
+
+Examples:
+
+- Ethereum EIPs repository
+- official pull request queue
+- canonical standards repo
+- maintainer-owned implementation repo used for amendments or reference updates
+
+## Icons
+
+Use one of the normalized icon names defined in `schema.ts`:
+
+- `shield`
+- `coins`
+- `scale`
+- `network`
+- `file-stack`
diff --git a/apps/portal-public/src/content/standards/filter.test.ts b/apps/portal-public/src/content/standards/filter.test.ts
new file mode 100644
index 0000000..9ce7c22
--- /dev/null
+++ b/apps/portal-public/src/content/standards/filter.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, it } from 'vitest';
+import { getSortedStandards } from './index';
+import { filterStandards } from './filter';
+
+describe('filterStandards', () => {
+ const standards = getSortedStandards();
+
+ it('filters by query', () => {
+ const result = filterStandards(standards, { q: 'compliant' });
+ expect(result.map((standard) => standard.slug)).toEqual(['erc-3643']);
+ });
+
+ it('filters by compliance mode', () => {
+ const result = filterStandards(standards, { compliance: 'external' });
+ expect(result.map((standard) => standard.slug)).toEqual(['erc-20']);
+ });
+
+ it('filters featured entries', () => {
+ const result = filterStandards(standards, { featured: 'true' });
+ expect(result.map((standard) => standard.slug)).toEqual(['erc-3643']);
+ });
+
+ it('applies a limit', () => {
+ const result = filterStandards(standards, { limit: 1 });
+ expect(result).toHaveLength(1);
+ });
+});
diff --git a/apps/portal-public/src/content/standards/filter.ts b/apps/portal-public/src/content/standards/filter.ts
new file mode 100644
index 0000000..30f47cc
--- /dev/null
+++ b/apps/portal-public/src/content/standards/filter.ts
@@ -0,0 +1,50 @@
+import type { StandardRegistryEntry } from './index';
+
+export interface StandardsFilterInput {
+ q?: string | null;
+ category?: string | null;
+ status?: string | null;
+ featured?: string | null;
+ compliance?: string | null;
+ limit?: number | null;
+}
+
+export function filterStandards(
+ standards: StandardRegistryEntry[],
+ filters: StandardsFilterInput
+): StandardRegistryEntry[] {
+ const query = filters.q?.trim().toLowerCase() ?? '';
+ const category = filters.category?.trim().toLowerCase() ?? '';
+ const status = filters.status?.trim().toLowerCase() ?? '';
+ const featured = filters.featured ?? null;
+ const compliance = filters.compliance?.trim().toLowerCase() ?? '';
+ const limit = filters.limit ?? null;
+
+ let filtered = standards.filter((standard) => {
+ if (query) {
+ const matchesQuery =
+ standard.name.toLowerCase().includes(query) ||
+ standard.summary.toLowerCase().includes(query) ||
+ standard.category.toLowerCase().includes(query) ||
+ (standard.shortName?.toLowerCase().includes(query) ?? false) ||
+ standard.standards.some((item) => item.toLowerCase().includes(query));
+
+ if (!matchesQuery) return false;
+ }
+
+ if (category && standard.category.toLowerCase() !== category) return false;
+ if (status && standard.status.toLowerCase() !== status) return false;
+ if (featured === 'true' && !standard.featured) return false;
+ if (featured === 'false' && standard.featured) return false;
+ if (compliance === 'native' && !standard.compliance.native) return false;
+ if (compliance === 'external' && standard.compliance.native) return false;
+
+ return true;
+ });
+
+ if (typeof limit === 'number' && Number.isFinite(limit) && limit > 0) {
+ filtered = filtered.slice(0, limit);
+ }
+
+ return filtered;
+}
diff --git a/apps/portal-public/src/content/standards/generate-artifacts.ts b/apps/portal-public/src/content/standards/generate-artifacts.ts
new file mode 100644
index 0000000..9ed2674
--- /dev/null
+++ b/apps/portal-public/src/content/standards/generate-artifacts.ts
@@ -0,0 +1,35 @@
+import { mkdir, writeFile } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { getSortedStandards } from './index';
+import { standardsRegistryJsonSchema } from './schema-json';
+
+async function main(): Promise {
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
+ const projectRoot = path.resolve(currentDir, '..', '..', '..');
+ const outputDir = path.join(projectRoot, 'public', 'standards');
+
+ await mkdir(outputDir, { recursive: true });
+
+ const standards = getSortedStandards();
+
+ await writeFile(
+ path.join(outputDir, 'registry.json'),
+ `${JSON.stringify({ standards, count: standards.length }, null, 2)}\n`,
+ 'utf8'
+ );
+
+ await writeFile(
+ path.join(outputDir, 'schema.json'),
+ `${JSON.stringify(standardsRegistryJsonSchema, null, 2)}\n`,
+ 'utf8'
+ );
+
+ process.stdout.write(`Generated ${path.join(outputDir, 'registry.json')}\n`);
+ process.stdout.write(`Generated ${path.join(outputDir, 'schema.json')}\n`);
+}
+
+main().catch((error) => {
+ process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
+ process.exitCode = 1;
+});
diff --git a/apps/portal-public/src/content/standards/index.test.ts b/apps/portal-public/src/content/standards/index.test.ts
new file mode 100644
index 0000000..16a359e
--- /dev/null
+++ b/apps/portal-public/src/content/standards/index.test.ts
@@ -0,0 +1,31 @@
+import { describe, expect, it } from 'vitest';
+import { getComparisonTarget, getFeaturedStandards, getRelatedStandards, getSortedStandards } from './index';
+
+describe('standards registry helpers', () => {
+ it('returns standards sorted by order', () => {
+ const standards = getSortedStandards();
+
+ expect(standards.map((standard) => standard.slug)).toEqual(['erc-3643', 'erc-20']);
+ });
+
+ it('returns featured standards', () => {
+ const featured = getFeaturedStandards();
+
+ expect(featured).toHaveLength(1);
+ expect(featured[0]?.slug).toBe('erc-3643');
+ });
+
+ it('resolves explicit comparison targets', () => {
+ const erc3643 = getSortedStandards()[0]!;
+ const comparisonTarget = getComparisonTarget(erc3643);
+
+ expect(comparisonTarget?.slug).toBe('erc-20');
+ });
+
+ it('returns related standards ordered by score', () => {
+ const erc3643 = getSortedStandards()[0]!;
+ const related = getRelatedStandards(erc3643);
+
+ expect(related.map((standard) => standard.slug)).toContain('erc-20');
+ });
+});
diff --git a/apps/portal-public/src/content/standards/index.ts b/apps/portal-public/src/content/standards/index.ts
new file mode 100644
index 0000000..98fcf8c
--- /dev/null
+++ b/apps/portal-public/src/content/standards/index.ts
@@ -0,0 +1,399 @@
+import type { MarketingLandingPageProps } from '../../components/marketing/landing-page';
+import { erc20LandingPage } from '../erc20';
+import { erc3643LandingPage } from '../erc3643';
+import { standardsRegistrySchema, type StandardRegistryEntrySchema } from './schema';
+
+export type StandardRegistryEntry = StandardRegistryEntrySchema & {
+ landingPage: MarketingLandingPageProps;
+};
+
+const standardsRegistrySource: StandardRegistryEntry[] = [
+ {
+ slug: 'erc-3643',
+ name: 'ERC-3643',
+ shortName: 'Compliant Tokenization',
+ eyebrow: 'Compliant Token Standard',
+ icon: 'shield',
+ summary:
+ 'Issue, manage, and distribute regulated digital assets with identity-based compliance enforced directly at the token level.',
+ category: 'Compliance and tokenization',
+ status: 'Live',
+ audience: 'Financial institutions, issuers, tokenization platforms, and regulated RWA teams.',
+ standards: ['ERC-3643', 'ERC-20'],
+ publisher: 'Tokeny',
+ auditor: 'Hacken',
+ seo: {
+ title: 'ERC-3643 | The Order',
+ description:
+ 'Compliant tokenization for real-world assets with identity-based compliance, lifecycle automation, and ERC-20 compatibility.',
+ keywords: ['ERC-3643', 'compliant tokenization', 'real-world assets', 'regulated digital assets'],
+ },
+ specification: {
+ label: 'EIP-3643 Specification',
+ href: 'https://eips.ethereum.org/EIPS/eip-3643',
+ kind: 'spec',
+ },
+ docs: [
+ {
+ label: 'Tokeny ERC-3643 Overview',
+ href: 'https://www.erc3643.org/',
+ kind: 'docs',
+ },
+ ],
+ repos: [
+ {
+ label: 'ethereum/EIPs',
+ href: 'https://github.com/ethereum/EIPs',
+ kind: 'repo',
+ },
+ {
+ label: 'TokenySolutions/T-REX',
+ href: 'https://github.com/TokenySolutions/T-REX',
+ kind: 'repo',
+ },
+ ],
+ submissionPoints: [
+ {
+ label: 'Ethereum EIPs Repository',
+ href: 'https://github.com/ethereum/EIPs',
+ kind: 'submission',
+ },
+ {
+ label: 'EIP-3643 Pull Requests',
+ href: 'https://github.com/ethereum/EIPs/pulls',
+ kind: 'submission',
+ },
+ ],
+ compliance: {
+ native: true,
+ label: 'Native compliance',
+ summary:
+ 'Identity, eligibility, and transfer rules are enforced directly in token behavior rather than being bolted on externally.',
+ enforcement: [
+ 'Identity-aware transfer restrictions',
+ 'Eligibility controls for holders and counterparties',
+ 'Lifecycle rule updates during issuance and administration',
+ ],
+ },
+ governanceBody: 'Ethereum Improvement Proposal process with ecosystem implementation led by Tokeny and related adopters.',
+ maintainers: [
+ {
+ name: 'Ethereum EIPs editors',
+ role: 'Canonical specification governance',
+ href: 'https://github.com/ethereum/EIPs',
+ },
+ {
+ name: 'Tokeny',
+ role: 'Reference implementation and ecosystem steward',
+ href: 'https://github.com/TokenySolutions/T-REX',
+ },
+ ],
+ statusHistory: [
+ {
+ date: '2021-11-09',
+ title: 'EIP-3643 created',
+ summary: 'The compliant tokenization standard was introduced through the Ethereum EIP process.',
+ },
+ {
+ date: '2022-01-26',
+ title: 'T-REX reference implementation publicized',
+ summary: 'Production-oriented implementation guidance and contracts became widely available through Tokeny.',
+ },
+ {
+ date: '2024-01-01',
+ title: 'Ecosystem adoption broadened',
+ summary: 'ERC-3643 continued to mature as a practical framework for regulated asset issuance and lifecycle management.',
+ },
+ ],
+ implementationExamples: [
+ {
+ label: 'Tokeny T-REX reference contracts',
+ summary: 'Production-oriented smart contract suite implementing identity-aware compliance controls for ERC-3643 assets.',
+ href: 'https://github.com/TokenySolutions/T-REX',
+ },
+ {
+ label: 'EIP-3643 canonical specification',
+ summary: 'The normative source for interface behavior, goals, and compliance-oriented design expectations.',
+ href: 'https://eips.ethereum.org/EIPS/eip-3643',
+ },
+ ],
+ submissionChecklist: [
+ {
+ label: 'Canonical EIP specification reference',
+ required: true,
+ summary: 'Point to the accepted EIP text and normative standard behavior.',
+ },
+ {
+ label: 'Reference implementation repository',
+ required: true,
+ summary: 'Include a public implementation repo that demonstrates how the standard is realized in production.',
+ },
+ {
+ label: 'Compliance posture declaration',
+ required: true,
+ summary: 'Document whether compliance is natively enforced or relies on external controls.',
+ },
+ {
+ label: 'Submission points and governance path',
+ required: true,
+ summary: 'Identify the repo, pull request surface, and governance venue for amendments or follow-on proposals.',
+ },
+ ],
+ ecosystemSupport: [
+ {
+ area: 'Wallet compatibility',
+ status: 'Moderate',
+ summary: 'Leverages ERC-20 compatibility, though full compliance-aware workflows depend on supporting infrastructure.',
+ },
+ {
+ area: 'Exchange and marketplace support',
+ status: 'Specialized',
+ summary: 'Best fit for venues prepared to handle regulated asset onboarding and rule-aware transfer flows.',
+ },
+ {
+ area: 'Issuer administration tooling',
+ status: 'Strong',
+ summary: 'Well suited to issuer-facing workflows, lifecycle events, and identity-driven operating models.',
+ },
+ {
+ area: 'Regulated asset operations',
+ status: 'Strong',
+ summary: 'Purpose-built for permissioned or regulated issuance where transfer and holder eligibility matter.',
+ },
+ ],
+ comparisonTarget: 'erc-20',
+ featured: true,
+ order: 10,
+ href: '/standards/erc-3643',
+ landingPage: erc3643LandingPage,
+ },
+ {
+ slug: 'erc-20',
+ name: 'ERC-20',
+ shortName: 'Fungible Token Baseline',
+ eyebrow: 'Foundational Token Standard',
+ icon: 'coins',
+ summary:
+ 'The core fungible token standard for broad wallet, exchange, and application compatibility across the Ethereum ecosystem.',
+ category: 'General-purpose tokenization',
+ status: 'Live',
+ audience: 'Protocol teams, issuers, exchanges, treasury teams, and builders focused on interoperability.',
+ standards: ['ERC-20'],
+ seo: {
+ title: 'ERC-20 | The Order',
+ description:
+ 'The foundational fungible token standard for broad wallet, exchange, and application compatibility across Ethereum.',
+ keywords: ['ERC-20', 'fungible token', 'Ethereum token standard', 'token interoperability'],
+ },
+ specification: {
+ label: 'EIP-20 Specification',
+ href: 'https://eips.ethereum.org/EIPS/eip-20',
+ kind: 'spec',
+ },
+ docs: [
+ {
+ label: 'OpenZeppelin ERC-20 Guide',
+ href: 'https://docs.openzeppelin.com/contracts/4.x/erc20',
+ kind: 'docs',
+ },
+ ],
+ repos: [
+ {
+ label: 'ethereum/EIPs',
+ href: 'https://github.com/ethereum/EIPs',
+ kind: 'repo',
+ },
+ {
+ label: 'OpenZeppelin/openzeppelin-contracts',
+ href: 'https://github.com/OpenZeppelin/openzeppelin-contracts',
+ kind: 'repo',
+ },
+ ],
+ submissionPoints: [
+ {
+ label: 'Ethereum EIPs Repository',
+ href: 'https://github.com/ethereum/EIPs',
+ kind: 'submission',
+ },
+ {
+ label: 'EIP-20 Pull Requests',
+ href: 'https://github.com/ethereum/EIPs/pulls',
+ kind: 'submission',
+ },
+ ],
+ compliance: {
+ native: false,
+ label: 'External compliance required',
+ summary:
+ 'ERC-20 does not natively enforce compliance. Regulated issuance typically depends on wrappers, policy layers, or additional contract systems.',
+ enforcement: [
+ 'External policy engines or offchain controls',
+ 'Custom transfer restrictions when needed',
+ 'Operational overlays for regulated counterparties',
+ ],
+ },
+ governanceBody: 'Ethereum Improvement Proposal process with widespread ecosystem stewardship across libraries, wallets, and exchanges.',
+ maintainers: [
+ {
+ name: 'Ethereum EIPs editors',
+ role: 'Canonical specification governance',
+ href: 'https://github.com/ethereum/EIPs',
+ },
+ {
+ name: 'OpenZeppelin',
+ role: 'Widely used implementation maintainer',
+ href: 'https://github.com/OpenZeppelin/openzeppelin-contracts',
+ },
+ ],
+ statusHistory: [
+ {
+ date: '2015-11-19',
+ title: 'EIP-20 proposed',
+ summary: 'The fungible token interface was introduced as a standard for interoperable Ethereum assets.',
+ },
+ {
+ date: '2017-09-20',
+ title: 'EIP-20 finalized',
+ summary: 'ERC-20 became the canonical baseline for fungible token behavior across the ecosystem.',
+ },
+ {
+ date: '2024-01-01',
+ title: 'Baseline ecosystem standard maintained',
+ summary: 'ERC-20 remains the reference layer for fungible token interoperability and exchange integration.',
+ },
+ ],
+ implementationExamples: [
+ {
+ label: 'OpenZeppelin ERC-20 implementation',
+ summary: 'Widely used and audited Solidity implementation for fungible token behavior.',
+ href: 'https://github.com/OpenZeppelin/openzeppelin-contracts',
+ },
+ {
+ label: 'EIP-20 canonical specification',
+ summary: 'Normative interface definition for fungible token balances, transfers, and approvals.',
+ href: 'https://eips.ethereum.org/EIPS/eip-20',
+ },
+ ],
+ submissionChecklist: [
+ {
+ label: 'Canonical EIP specification reference',
+ required: true,
+ summary: 'Link the accepted EIP text as the normative basis for the registry entry.',
+ },
+ {
+ label: 'Reference implementation repository',
+ required: true,
+ summary: 'Provide at least one widely adopted implementation repo to anchor ecosystem usage.',
+ },
+ {
+ label: 'Compliance dependency disclosure',
+ required: true,
+ summary: 'State clearly that compliance is external if the token standard does not enforce it natively.',
+ },
+ {
+ label: 'Submission and amendment path',
+ required: true,
+ summary: 'Document the repository and PR path for future revisions to the standard.',
+ },
+ ],
+ ecosystemSupport: [
+ {
+ area: 'Wallet compatibility',
+ status: 'Strong',
+ summary: 'Near-universal support across Ethereum wallets and custody providers.',
+ },
+ {
+ area: 'Exchange and marketplace support',
+ status: 'Strong',
+ summary: 'Deep support across centralized and decentralized trading venues.',
+ },
+ {
+ area: 'Issuer administration tooling',
+ status: 'Moderate',
+ summary: 'Strong for basic issuance, though advanced compliance workflows usually require add-on systems.',
+ },
+ {
+ area: 'Regulated asset operations',
+ status: 'Specialized',
+ summary: 'Can be used as a base layer, but regulated issuance typically needs additional policy and control infrastructure.',
+ },
+ ],
+ comparisonTarget: 'erc-3643',
+ featured: false,
+ order: 20,
+ href: '/standards/erc-20',
+ landingPage: erc20LandingPage,
+ },
+];
+
+export const standardsRegistry = standardsRegistrySchema.parse(standardsRegistrySource) as StandardRegistryEntry[];
+
+export function getSortedStandards(): StandardRegistryEntry[] {
+ return [...standardsRegistry].sort((left, right) => {
+ if (left.order !== right.order) {
+ return left.order - right.order;
+ }
+
+ return left.name.localeCompare(right.name);
+ });
+}
+
+export function getFeaturedStandards(): StandardRegistryEntry[] {
+ return getSortedStandards().filter((entry) => entry.featured);
+}
+
+export function getStandardBySlug(slug: string): StandardRegistryEntry | undefined {
+ return standardsRegistry.find((entry) => entry.slug === slug);
+}
+
+export function getComparisonTarget(standard: StandardRegistryEntry): StandardRegistryEntry | undefined {
+ if (!standard.comparisonTarget) {
+ return undefined;
+ }
+
+ return getStandardBySlug(standard.comparisonTarget);
+}
+
+export function getRelatedStandards(
+ standard: StandardRegistryEntry,
+ limit = 2
+): StandardRegistryEntry[] {
+ const others = getSortedStandards().filter((entry) => entry.slug !== standard.slug);
+
+ const scored = others.map((entry) => {
+ let score = 0;
+
+ if (entry.slug === standard.comparisonTarget) {
+ score += 100;
+ }
+
+ if (entry.comparisonTarget === standard.slug) {
+ score += 80;
+ }
+
+ if (entry.category === standard.category) {
+ score += 30;
+ }
+
+ const sharedStandards = entry.standards.filter((item) => standard.standards.includes(item)).length;
+ score += sharedStandards * 10;
+
+ if (entry.compliance.native === standard.compliance.native) {
+ score += 5;
+ }
+
+ return { entry, score };
+ });
+
+ return scored
+ .filter((item) => item.score > 0)
+ .sort((left, right) => {
+ if (left.score !== right.score) {
+ return right.score - left.score;
+ }
+
+ return left.entry.order - right.entry.order;
+ })
+ .slice(0, limit)
+ .map((item) => item.entry);
+}
diff --git a/apps/portal-public/src/content/standards/schema-json.ts b/apps/portal-public/src/content/standards/schema-json.ts
new file mode 100644
index 0000000..6eea0f8
--- /dev/null
+++ b/apps/portal-public/src/content/standards/schema-json.ts
@@ -0,0 +1,227 @@
+export const standardsRegistryJsonSchema = {
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
+ $id: 'https://the-order.local/standards/schema.json',
+ title: 'The Order Standards Registry',
+ description: 'Canonical schema for standards registry entries used by the public portal.',
+ type: 'array',
+ items: {
+ $ref: '#/$defs/standardRegistryEntry',
+ },
+ $defs: {
+ standardIcon: {
+ type: 'string',
+ enum: ['shield', 'coins', 'scale', 'network', 'file-stack'],
+ },
+ standardLink: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['label', 'href', 'kind'],
+ properties: {
+ label: { type: 'string', minLength: 1 },
+ href: { type: 'string', format: 'uri' },
+ kind: { type: 'string', enum: ['spec', 'repo', 'docs', 'submission'] },
+ },
+ },
+ seo: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['title', 'description'],
+ properties: {
+ title: { type: 'string', minLength: 1 },
+ description: { type: 'string', minLength: 1 },
+ keywords: {
+ type: 'array',
+ items: { type: 'string', minLength: 1 },
+ },
+ },
+ },
+ complianceProfile: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['native', 'label', 'summary', 'enforcement'],
+ properties: {
+ native: { type: 'boolean' },
+ label: { type: 'string', minLength: 1 },
+ summary: { type: 'string', minLength: 1 },
+ enforcement: {
+ type: 'array',
+ minItems: 1,
+ items: { type: 'string', minLength: 1 },
+ },
+ },
+ },
+ maintainer: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['name', 'role'],
+ properties: {
+ name: { type: 'string', minLength: 1 },
+ role: { type: 'string', minLength: 1 },
+ href: { type: 'string', format: 'uri' },
+ },
+ },
+ statusEvent: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['date', 'title', 'summary'],
+ properties: {
+ date: { type: 'string', minLength: 1 },
+ title: { type: 'string', minLength: 1 },
+ summary: { type: 'string', minLength: 1 },
+ },
+ },
+ implementationExample: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['label', 'summary', 'href'],
+ properties: {
+ label: { type: 'string', minLength: 1 },
+ summary: { type: 'string', minLength: 1 },
+ href: { type: 'string', format: 'uri' },
+ },
+ },
+ checklistItem: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['label', 'required', 'summary'],
+ properties: {
+ label: { type: 'string', minLength: 1 },
+ required: { type: 'boolean' },
+ summary: { type: 'string', minLength: 1 },
+ },
+ },
+ ecosystemSupport: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['area', 'status', 'summary'],
+ properties: {
+ area: { type: 'string', minLength: 1 },
+ status: { type: 'string', enum: ['Strong', 'Moderate', 'Specialized'] },
+ summary: { type: 'string', minLength: 1 },
+ },
+ },
+ marketingLink: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['label', 'href'],
+ properties: {
+ label: { type: 'string', minLength: 1 },
+ href: { type: 'string', minLength: 1 },
+ },
+ },
+ standardRegistryEntry: {
+ type: 'object',
+ additionalProperties: false,
+ required: [
+ 'slug',
+ 'name',
+ 'icon',
+ 'summary',
+ 'category',
+ 'status',
+ 'audience',
+ 'standards',
+ 'seo',
+ 'specification',
+ 'repos',
+ 'submissionPoints',
+ 'compliance',
+ 'governanceBody',
+ 'maintainers',
+ 'statusHistory',
+ 'implementationExamples',
+ 'submissionChecklist',
+ 'ecosystemSupport',
+ 'order',
+ 'href',
+ 'landingPage',
+ ],
+ properties: {
+ slug: { type: 'string', minLength: 1 },
+ name: { type: 'string', minLength: 1 },
+ shortName: { type: 'string', minLength: 1 },
+ eyebrow: { type: 'string', minLength: 1 },
+ icon: { $ref: '#/$defs/standardIcon' },
+ summary: { type: 'string', minLength: 1 },
+ category: { type: 'string', minLength: 1 },
+ status: { type: 'string', enum: ['Live', 'Preview'] },
+ audience: { type: 'string', minLength: 1 },
+ standards: {
+ type: 'array',
+ minItems: 1,
+ items: { type: 'string', minLength: 1 },
+ },
+ publisher: { type: 'string', minLength: 1 },
+ auditor: { type: 'string', minLength: 1 },
+ seo: { $ref: '#/$defs/seo' },
+ specification: { $ref: '#/$defs/standardLink' },
+ docs: {
+ type: 'array',
+ items: { $ref: '#/$defs/standardLink' },
+ },
+ repos: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/standardLink' },
+ },
+ submissionPoints: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/standardLink' },
+ },
+ compliance: { $ref: '#/$defs/complianceProfile' },
+ governanceBody: { type: 'string', minLength: 1 },
+ maintainers: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/maintainer' },
+ },
+ statusHistory: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/statusEvent' },
+ },
+ implementationExamples: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/implementationExample' },
+ },
+ submissionChecklist: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/checklistItem' },
+ },
+ ecosystemSupport: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#/$defs/ecosystemSupport' },
+ },
+ comparisonTarget: { type: 'string', minLength: 1 },
+ featured: { type: 'boolean' },
+ order: { type: 'integer', minimum: 0 },
+ href: { type: 'string', minLength: 1 },
+ landingPage: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['hero'],
+ properties: {
+ hero: {
+ type: 'object',
+ additionalProperties: false,
+ required: ['headline', 'subheadline'],
+ properties: {
+ eyebrow: { type: 'string' },
+ headline: { type: 'string' },
+ subheadline: { type: 'string' },
+ supportingCopy: { type: 'string' },
+ primaryCta: { $ref: '#/$defs/marketingLink' },
+ secondaryCta: { $ref: '#/$defs/marketingLink' },
+ tertiaryLink: { $ref: '#/$defs/marketingLink' },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+} as const;
diff --git a/apps/portal-public/src/content/standards/schema.ts b/apps/portal-public/src/content/standards/schema.ts
new file mode 100644
index 0000000..3be5ddf
--- /dev/null
+++ b/apps/portal-public/src/content/standards/schema.ts
@@ -0,0 +1,151 @@
+import { z } from 'zod';
+
+export const standardIconSchema = z.enum(['shield', 'coins', 'scale', 'network', 'file-stack']);
+
+export const standardLinkSchema = z.object({
+ label: z.string().min(1),
+ href: z.string().url(),
+ kind: z.enum(['spec', 'repo', 'docs', 'submission']),
+});
+
+export const standardSeoSchema = z.object({
+ title: z.string().min(1),
+ description: z.string().min(1),
+ keywords: z.array(z.string().min(1)).optional(),
+});
+
+export const standardComplianceProfileSchema = z.object({
+ native: z.boolean(),
+ label: z.string().min(1),
+ summary: z.string().min(1),
+ enforcement: z.array(z.string().min(1)).min(1),
+});
+
+export const standardMaintainerSchema = z.object({
+ name: z.string().min(1),
+ role: z.string().min(1),
+ href: z.string().url().optional(),
+});
+
+export const standardStatusEventSchema = z.object({
+ date: z.string().min(1),
+ title: z.string().min(1),
+ summary: z.string().min(1),
+});
+
+export const standardImplementationExampleSchema = z.object({
+ label: z.string().min(1),
+ summary: z.string().min(1),
+ href: z.string().url(),
+});
+
+export const standardChecklistItemSchema = z.object({
+ label: z.string().min(1),
+ required: z.boolean(),
+ summary: z.string().min(1),
+});
+
+export const standardEcosystemSupportItemSchema = z.object({
+ area: z.string().min(1),
+ status: z.enum(['Strong', 'Moderate', 'Specialized']),
+ summary: z.string().min(1),
+});
+
+export const standardRegistryEntrySchema = z.object({
+ slug: z.string().min(1),
+ name: z.string().min(1),
+ shortName: z.string().min(1).optional(),
+ eyebrow: z.string().min(1).optional(),
+ icon: standardIconSchema,
+ summary: z.string().min(1),
+ category: z.string().min(1),
+ status: z.enum(['Live', 'Preview']),
+ audience: z.string().min(1),
+ standards: z.array(z.string().min(1)).min(1),
+ publisher: z.string().min(1).optional(),
+ auditor: z.string().min(1).optional(),
+ seo: standardSeoSchema,
+ specification: standardLinkSchema,
+ docs: z.array(standardLinkSchema).optional(),
+ repos: z.array(standardLinkSchema).min(1),
+ submissionPoints: z.array(standardLinkSchema).min(1),
+ compliance: standardComplianceProfileSchema,
+ governanceBody: z.string().min(1),
+ maintainers: z.array(standardMaintainerSchema).min(1),
+ statusHistory: z.array(standardStatusEventSchema).min(1),
+ implementationExamples: z.array(standardImplementationExampleSchema).min(1),
+ submissionChecklist: z.array(standardChecklistItemSchema).min(1),
+ ecosystemSupport: z.array(standardEcosystemSupportItemSchema).min(1),
+ comparisonTarget: z.string().min(1).optional(),
+ featured: z.boolean().optional(),
+ order: z.number().int().nonnegative(),
+ href: z.string().min(1),
+ landingPage: z.object({
+ hero: z.object({
+ eyebrow: z.string().optional(),
+ headline: z.string(),
+ subheadline: z.string(),
+ supportingCopy: z.string().optional(),
+ primaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
+ secondaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
+ tertiaryLink: z.object({ label: z.string(), href: z.string() }).optional(),
+ }),
+ trustStrip: z
+ .object({
+ label: z.string().optional(),
+ items: z.array(z.object({ label: z.string(), value: z.string() })),
+ })
+ .optional(),
+ valueProps: z
+ .object({
+ headline: z.string(),
+ intro: z.string().optional(),
+ cards: z.array(z.object({ title: z.string(), copy: z.string() })),
+ })
+ .optional(),
+ useCases: z
+ .object({
+ headline: z.string(),
+ intro: z.string().optional(),
+ items: z.array(z.string()),
+ supportingLine: z.string().optional(),
+ })
+ .optional(),
+ proofPoints: z
+ .object({
+ headline: z.string(),
+ intro: z.string().optional(),
+ items: z.array(z.string()),
+ })
+ .optional(),
+ comparison: z
+ .object({
+ headline: z.string(),
+ intro: z.string().optional(),
+ columns: z.tuple([z.string(), z.string(), z.string()]),
+ rows: z.array(z.object({ category: z.string(), left: z.string(), right: z.string() })),
+ secondaryNote: z.string().optional(),
+ cta: z.object({ label: z.string(), href: z.string() }).optional(),
+ })
+ .optional(),
+ faq: z
+ .object({
+ headline: z.string(),
+ items: z.array(z.object({ question: z.string(), answer: z.string() })),
+ })
+ .optional(),
+ footerCta: z
+ .object({
+ headline: z.string(),
+ copy: z.string(),
+ primaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
+ secondaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
+ })
+ .optional(),
+ }),
+});
+
+export const standardsRegistrySchema = z.array(standardRegistryEntrySchema);
+
+export type StandardIconName = z.infer;
+export type StandardRegistryEntrySchema = z.infer;
diff --git a/apps/portal-public/src/content/standards/template.ts b/apps/portal-public/src/content/standards/template.ts
new file mode 100644
index 0000000..3bc2550
--- /dev/null
+++ b/apps/portal-public/src/content/standards/template.ts
@@ -0,0 +1,99 @@
+import type { StandardRegistryEntry } from './index';
+
+export const standardsRegistryTemplate: StandardRegistryEntry = {
+ slug: 'new-standard-slug',
+ name: 'New Standard',
+ shortName: 'Short positioning label',
+ eyebrow: 'Optional eyebrow',
+ icon: 'file-stack',
+ summary: 'One-sentence summary for cards, search, SEO previews, and registry exports.',
+ category: 'Category label',
+ status: 'Preview',
+ audience: 'Who this standard is for.',
+ standards: ['PRIMARY-STANDARD-ID'],
+ publisher: 'Optional publisher',
+ auditor: 'Optional auditor',
+ seo: {
+ title: 'New Standard | The Order',
+ description: 'SEO description for the detail page.',
+ keywords: ['keyword-one', 'keyword-two'],
+ },
+ specification: {
+ label: 'Canonical specification',
+ href: 'https://example.com/spec',
+ kind: 'spec',
+ },
+ docs: [
+ {
+ label: 'Documentation link',
+ href: 'https://example.com/docs',
+ kind: 'docs',
+ },
+ ],
+ repos: [
+ {
+ label: 'Reference repo',
+ href: 'https://github.com/example/repo',
+ kind: 'repo',
+ },
+ ],
+ submissionPoints: [
+ {
+ label: 'Canonical submission point',
+ href: 'https://github.com/example/repo/pulls',
+ kind: 'submission',
+ },
+ ],
+ compliance: {
+ native: false,
+ label: 'External compliance required',
+ summary: 'State clearly whether compliance is native or external.',
+ enforcement: ['Describe enforcement mechanism'],
+ },
+ governanceBody: 'Who governs the standard and how changes are made.',
+ maintainers: [
+ {
+ name: 'Maintainer name',
+ role: 'Maintainer role',
+ href: 'https://example.com/maintainer',
+ },
+ ],
+ statusHistory: [
+ {
+ date: 'YYYY-MM-DD',
+ title: 'Milestone title',
+ summary: 'Why this milestone matters.',
+ },
+ ],
+ implementationExamples: [
+ {
+ label: 'Example implementation',
+ summary: 'Why this implementation is relevant.',
+ href: 'https://example.com/example',
+ },
+ ],
+ submissionChecklist: [
+ {
+ label: 'Checklist item',
+ required: true,
+ summary: 'What this item proves or covers.',
+ },
+ ],
+ ecosystemSupport: [
+ {
+ area: 'Wallet compatibility',
+ status: 'Moderate',
+ summary: 'How well the standard is supported here.',
+ },
+ ],
+ comparisonTarget: 'existing-standard-slug',
+ featured: false,
+ order: 999,
+ href: '/standards/new-standard-slug',
+ landingPage: {
+ hero: {
+ headline: 'Hero headline',
+ subheadline: 'Hero subheadline',
+ },
+ },
+};
diff --git a/apps/portal-public/src/lib/auth.ts b/apps/portal-public/src/lib/auth.ts
index 6d07169..56d6fad 100644
--- a/apps/portal-public/src/lib/auth.ts
+++ b/apps/portal-public/src/lib/auth.ts
@@ -87,6 +87,16 @@ class AuthStore {
const authStore = typeof window !== 'undefined' ? new AuthStore() : null;
+function writeAuthCookie(accessToken?: string): void {
+ if (typeof document === 'undefined') return;
+ if (!accessToken) {
+ document.cookie = 'auth_token=; Path=/; Max-Age=0; SameSite=Lax';
+ return;
+ }
+
+ document.cookie = `auth_token=${encodeURIComponent(accessToken)}; Path=/; Max-Age=2592000; SameSite=Lax`;
+}
+
// React hook to use auth state
export function useAuth(): AuthState {
const [state, setState] = React.useState(
@@ -114,13 +124,16 @@ export function useAuth(): AuthState {
if (user.accessToken) {
localStorage.setItem('auth_token', user.accessToken);
}
+ writeAuthCookie(user.accessToken);
},
logout: () => {
authStore?.setState({ user: null, isAuthenticated: false });
localStorage.removeItem('auth_token');
+ writeAuthCookie();
},
setUser: (user: AuthUser | null) => {
authStore?.setState({ user, isAuthenticated: !!user });
+ writeAuthCookie(user?.accessToken);
},
};
}
diff --git a/apps/portal-public/src/lib/ui-server.ts b/apps/portal-public/src/lib/ui-server.ts
new file mode 100644
index 0000000..d90de45
--- /dev/null
+++ b/apps/portal-public/src/lib/ui-server.ts
@@ -0,0 +1 @@
+export * from '../../../../packages/ui/src/server';
diff --git a/apps/portal-public/src/middleware.ts b/apps/portal-public/src/middleware.ts
index cf3ab13..2b19acb 100644
--- a/apps/portal-public/src/middleware.ts
+++ b/apps/portal-public/src/middleware.ts
@@ -4,7 +4,7 @@ import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest): NextResponse {
// Public portal routes are generally open
// Only protect specific routes if needed
- const protectedRoutes: string[] = []; // Add protected routes here if needed
+ const protectedRoutes: string[] = ['/standards/admin'];
const isProtectedRoute = protectedRoutes.some((route) => request.nextUrl.pathname.startsWith(route));
@@ -22,6 +22,5 @@ export function middleware(request: NextRequest): NextResponse {
}
export const config = {
- matcher: [], // Add protected routes here if needed
+ matcher: ['/standards/admin/:path*'],
};
-
diff --git a/apps/portal-public/tsconfig.json b/apps/portal-public/tsconfig.json
index 4d4a47e..0f66e0c 100644
--- a/apps/portal-public/tsconfig.json
+++ b/apps/portal-public/tsconfig.json
@@ -3,6 +3,7 @@
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
+ "rootDir": "../../",
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -22,10 +23,17 @@
"paths": {
"@/*": ["./src/*"],
"@the-order/ui": ["../../packages/ui/src"],
+ "@the-order/ui/server": ["../../packages/ui/src/server"],
"@the-order/schemas": ["../../packages/schemas/src"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ "../../packages/ui/src/**/*.ts",
+ "../../packages/ui/src/**/*.tsx"
+ ],
+ "exclude": ["node_modules", "**/*.test.ts", "vitest.config.mts"]
}
-
diff --git a/apps/portal-public/vitest.config.mts b/apps/portal-public/vitest.config.mts
new file mode 100644
index 0000000..7eeb3f8
--- /dev/null
+++ b/apps/portal-public/vitest.config.mts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ environment: 'node',
+ include: ['src/**/*.test.ts'],
+ },
+});
diff --git a/packages/ui/src/server.ts b/packages/ui/src/server.ts
new file mode 100644
index 0000000..26bf5e0
--- /dev/null
+++ b/packages/ui/src/server.ts
@@ -0,0 +1,23 @@
+/**
+ * Server-safe UI exports.
+ *
+ * Keep this entrypoint limited to components and utilities that do not depend
+ * on client-only hooks or browser APIs so Next.js server components can import
+ * from it safely.
+ */
+
+export { Button } from './components/Button';
+export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './components/Card';
+export { Input } from './components/Input';
+export { Label } from './components/Label';
+export { Select } from './components/Select';
+export { Textarea } from './components/Textarea';
+export { Alert, AlertTitle, AlertDescription } from './components/Alert';
+export { Badge } from './components/Badge';
+export { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from './components/Table';
+export { Skeleton } from './components/Skeleton';
+export { Breadcrumbs } from './components/Breadcrumbs';
+export { Checkbox } from './components/Checkbox';
+export { Radio } from './components/Radio';
+export { Switch } from './components/Switch';
+export { cn } from './lib/utils';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f227580..e760936 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -175,7 +175,7 @@ importers:
version: 8.5.6
tailwindcss:
specifier: ^3.4.1
- version: 3.4.18
+ version: 3.4.18(tsx@4.20.6)
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.18)
@@ -249,6 +249,9 @@ importers:
autoprefixer:
specifier: ^10.4.16
version: 10.4.22(postcss@8.5.6)
+ baseline-browser-mapping:
+ specifier: ^2.10.19
+ version: 2.10.19
eslint:
specifier: ^8.57.1
version: 8.57.1
@@ -260,13 +263,19 @@ importers:
version: 8.5.6
tailwindcss:
specifier: ^3.4.1
- version: 3.4.18
+ version: 3.4.18(tsx@4.20.6)
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.18)
+ tsx:
+ specifier: ^4.20.5
+ version: 4.20.6
typescript:
specifier: ^5.3.3
version: 5.9.3
+ vitest:
+ specifier: ^1.6.1
+ version: 1.6.1(@types/node@20.19.24)(@vitest/ui@1.6.1)
packages/api-client:
dependencies:
@@ -6495,8 +6504,9 @@ packages:
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
- /baseline-browser-mapping@2.8.27:
- resolution: {integrity: sha512-2CXFpkjVnY2FT+B6GrSYxzYf65BJWEqz5tIRHCvNsZZ2F3CmsCB37h8SpYgKG7y9C4YAeTipIPWG7EmFmhAeXA==}
+ /baseline-browser-mapping@2.10.19:
+ resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==}
+ engines: {node: '>=6.0.0'}
hasBin: true
dev: true
@@ -6563,7 +6573,7 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- baseline-browser-mapping: 2.8.27
+ baseline-browser-mapping: 2.10.19
caniuse-lite: 1.0.30001754
electron-to-chromium: 1.5.250
node-releases: 2.0.27
@@ -10324,7 +10334,7 @@ packages:
postcss: 8.5.6
dev: true
- /postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6):
+ /postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6):
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
engines: {node: '>= 18'}
peerDependencies:
@@ -10345,6 +10355,7 @@ packages:
jiti: 1.21.7
lilconfig: 3.1.3
postcss: 8.5.6
+ tsx: 4.20.6
dev: true
/postcss-nested@6.2.0(postcss@8.5.6):
@@ -11471,10 +11482,10 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
- tailwindcss: 3.4.18
+ tailwindcss: 3.4.18(tsx@4.20.6)
dev: true
- /tailwindcss@3.4.18:
+ /tailwindcss@3.4.18(tsx@4.20.6):
resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -11496,7 +11507,7 @@ packages:
postcss: 8.5.6
postcss-import: 15.1.0(postcss@8.5.6)
postcss-js: 4.1.0(postcss@8.5.6)
- postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)
+ postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)
postcss-nested: 6.2.0(postcss@8.5.6)
postcss-selector-parser: 6.1.2
resolve: 1.22.11
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 0119d0c..7a19852 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -30,6 +30,7 @@
"baseUrl": ".",
"paths": {
"@the-order/ui": ["./packages/ui/src"],
+ "@the-order/ui/server": ["./packages/ui/src/server"],
"@the-order/schemas": ["./packages/schemas/src"],
"@the-order/auth": ["./packages/auth/src"],
"@the-order/storage": ["./packages/storage/src"],
@@ -46,12 +47,10 @@
"@the-order/notifications": ["./packages/notifications/src"],
"@the-order/eu-lp": ["./packages/eu-lp/src"],
"@the-order/cache": ["./packages/cache/src"],
- "@the-order/secrets": ["./packages/secrets/src"],
- "@the-order/workflows": ["./packages/workflows/src"],
- "@the-order/verifier-sdk": ["./packages/verifier-sdk/src"],
- "@the-order/api-client": ["./packages/api-client/src"]
- }
+ "@the-order/secrets": ["./packages/secrets/src"],
+ "@the-order/verifier-sdk": ["./packages/verifier-sdk/src"],
+ "@the-order/api-client": ["./packages/api-client/src"]
+ }
},
"exclude": ["node_modules", "dist", "build", ".next", "coverage"]
}
-