this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Restructure docs around "For users / Under the hood / For developers"

The flat docs tree mixed three audiences on one page — casual users who
want to try Opake, readers who want to know how it works, and developers
who want to build on it. Forcing every visitor through the same card grid
meant the casual user scrolled past "X25519 key wrapping" on their way to
"Getting Started", and the developer scrolled past "How to share with a
friend" to find the CLI reference.

Restructured the content tree to match intent:

content/docs/
index.mdx (rewritten landing)
use/ "For users" — store, share, recover
getting-started.mdx
pairing.mdx
seed-phrase.mdx
sharing.mdx (was sharing-dids — dropped the jargon)
workspaces.mdx (was keyrings — matches the web UI)
troubleshooting.mdx (moved out of top level)
understand/ "Under the hood" — crypto, records, protocol
encryption.mdx (was encryption-keys)
at-protocol.mdx
glossary.mdx
build/ "For developers" — CLI, SDK, lexicons
cli.mdx
faq.mdx (cross-cutting, stays near the top of the landing)

Note "Under the hood" deliberately breaks the "For X" pattern: the section
is about the content, not the audience, so anyone curious enough to read
doesn't have to decide whether they qualify as a developer first.

- `docs-registry.ts` gains a `category` field and a `docsByCategory`
helper. `CATEGORY_META` is the source of truth for the section labels
and descriptions that render on the landing.
- Both `$slug` routes (public + cabinet) update their MDX imports.
- The cabinet docs index groups cards by category and puts two entry
points at the top — a big primary "Just getting started?" CTA and a
slim secondary "Got a specific question? → FAQ" row, so visitors who
don't know which category fits them have obvious next clicks before
ever hitting the section grids.
- Public landing (`content/docs/index.mdx`) mirrors the same top-of-page
structure with new `<DocsIndexPrimary>` and `<DocsIndexSecondary>`
components, then three `<DocsIndexSection>` blocks — one per category.
- Cross-doc links updated for the renamed slugs (sharing-dids →
sharing, encryption-keys → encryption).
- CLI command examples in cli.mdx + keyrings.mdx (now workspaces.mdx)
swept for the `opake workspace` / `opake account` / `opake share` /
`--workspace` subcommand shape introduced in the monorepo rework.
- `glossary.mdx`: fixed a tautology — the Indexer entry said it fills
the atproto "indexer" role; corrected to "appview".

+436 -187
+97
apps/web/src/components/content/docs.tsx
··· 68 68 export function DocsIndexBody({ children }: ChildrenProps) { 69 69 return <p className="text-text-muted text-ui leading-relaxed">{children}</p>; 70 70 } 71 + 72 + /* ─── Primary CTA — "Just getting started?" ──────────────────────────────── */ 73 + 74 + interface DocsIndexPrimaryProps { 75 + readonly href: string; 76 + readonly icon: string; 77 + readonly title: string; 78 + readonly body: string; 79 + } 80 + 81 + /** 82 + * Big, opinionated card at the top of the docs landing. Catches visitors 83 + * who don't yet know which audience they belong to — clicking drops them 84 + * into "Getting Started" directly, no classification required. 85 + */ 86 + export function DocsIndexPrimary({ href, icon, title, body }: DocsIndexPrimaryProps) { 87 + return ( 88 + <div className="mx-auto mb-10 max-w-4xl"> 89 + <Link 90 + to={href} 91 + className="border-primary/40 bg-base-100 group hover:border-primary/70 hover:shadow-panel flex items-center gap-5 rounded-2xl border-2 p-6 transition-all" 92 + > 93 + <div className="bg-primary/15 flex size-14 shrink-0 items-center justify-center rounded-xl"> 94 + {createElement(resolveIcon(icon), { size: 26, className: "text-primary" })} 95 + </div> 96 + <div className="flex-1"> 97 + <h2 className="text-base-content mb-1 text-lg font-medium">{title}</h2> 98 + <p className="text-text-muted leading-relaxed">{body}</p> 99 + </div> 100 + <ArrowRightIcon 101 + size={20} 102 + className="text-primary transition-transform group-hover:translate-x-1" 103 + /> 104 + </Link> 105 + </div> 106 + ); 107 + } 108 + 109 + /* ─── Secondary CTA — quiet inline link for confused visitors ───────────── */ 110 + 111 + interface DocsIndexSecondaryProps { 112 + readonly href: string; 113 + readonly icon: string; 114 + readonly label: string; 115 + readonly body: string; 116 + } 117 + 118 + /** 119 + * Slim companion to {@link DocsIndexPrimary}. Use for entry points that 120 + * aren't the main "try Opake" action but still catch early confusion — the 121 + * FAQ being the obvious one. Rendered as a muted bar under the primary CTA 122 + * so it doesn't compete for the user's first click. 123 + */ 124 + export function DocsIndexSecondary({ href, icon, label, body }: DocsIndexSecondaryProps) { 125 + return ( 126 + <div className="mx-auto mb-10 max-w-4xl"> 127 + <Link 128 + to={href} 129 + className="border-border-accent/40 bg-base-100/60 group hover:border-primary/60 hover:bg-base-100 flex items-center gap-3 rounded-xl border p-3.5 transition-all" 130 + > 131 + <div className="bg-accent/60 flex size-8 shrink-0 items-center justify-center rounded-lg"> 132 + {createElement(resolveIcon(icon), { size: 15, className: "text-primary" })} 133 + </div> 134 + <div className="flex-1"> 135 + <span className="text-base-content text-ui font-medium">{label}</span> 136 + <span className="text-text-muted text-ui ml-1.5">{body}</span> 137 + </div> 138 + <ArrowRightIcon 139 + size={14} 140 + className="text-text-muted group-hover:text-primary transition-colors" 141 + /> 142 + </Link> 143 + </div> 144 + ); 145 + } 146 + 147 + /* ─── Section wrapper — "For users", "Under the hood", etc. ─────────────── */ 148 + 149 + interface DocsIndexSectionProps { 150 + readonly label: string; 151 + readonly description?: string; 152 + readonly children: ReactNode; 153 + } 154 + 155 + export function DocsIndexSection({ label, description, children }: DocsIndexSectionProps) { 156 + return ( 157 + <section className="mx-auto mt-10 max-w-4xl"> 158 + <div className="mb-4"> 159 + <h2 className="text-base-content text-[1.1rem] font-medium">{label}</h2> 160 + {description && ( 161 + <p className="text-text-muted text-ui mt-0.5 leading-relaxed">{description}</p> 162 + )} 163 + </div> 164 + {children} 165 + </section> 166 + ); 167 + }
+3
apps/web/src/components/content/index.ts
··· 27 27 DocsIndexCard, 28 28 DocsIndexTitle, 29 29 DocsIndexBody, 30 + DocsIndexPrimary, 31 + DocsIndexSecondary, 32 + DocsIndexSection, 30 33 } from "./docs"; 31 34 32 35 export {
+23 -20
apps/web/src/content/docs/at-protocol.mdx apps/web/src/content/docs/understand/at-protocol.mdx
··· 1 1 <ChapterHeader title="The Open Network: Your Data, Anywhere" /> 2 2 3 3 <Lead> 4 - Opake isn't a walled garden; it's a resident of the AT Protocol—a growing network of independent 5 - services. This means your data isn't trapped in a silo; it lives on a foundation that you control. 4 + Opake isn't a walled garden; it's a resident of the Atmosphere — the open network built on the 5 + AT Protocol. Your data doesn't sit in a silo; it lives on a foundation you control, alongside 6 + everyone else's. 6 7 </Lead> 7 8 8 - ## The Context: Why We Built This 9 + ## Why Opake was built on the Atmosphere 9 10 10 - The "Social Web" as we knew it is broken. For years, we lived in digital fiefdoms where a single 11 - company could decide the fate of our data, our identities, and our connections. Elon Musk decided to buy Twitter 12 - and use it to destroy online communities and spread an agenda of hat. 11 + The social web most of us grew up with is broken. For years we lived in digital fiefdoms where a 12 + single company could decide the fate of our data, our identities, and our connections. When Elon 13 + Musk bought Twitter and spent two years dismantling its safety and moderation systems, the urgency 14 + of a real alternative became undeniable. 13 15 14 - The urgency for a decentralized alternative became undeniable when major platforms began 15 - dismantling safety and moderation systems in favor of extremist ideologies. The goal of the 16 - AT Protocol is simple: to ensure that no single person or corporation can ever "own" your 17 - digital life again. 16 + The AT Protocol — and the wider Atmosphere around it — is one answer. It's an open, federated 17 + protocol: no single person or corporation gets to own your digital life. Opake didn't invent any 18 + of that. We picked it as our foundation because it already solves the hard parts of identity, 19 + portability, and federation, and because building on top of an ecosystem is a better long-term bet 20 + than trying to run our own silo. 18 21 19 - ### Breaking the Silo 22 + ### Breaking the silo 20 23 21 24 In the traditional cloud (Google Drive, Dropbox, iCloud), your identity and your storage are 22 25 permanently fused together. If you want to leave, you have to pack your bags, download 23 - everything, and "re-upload" it somewhere else. 26 + everything, and re-upload it somewhere else. 24 27 25 - Opake uses the **AT Protocol** to break this cycle. 28 + Opake uses the AT Protocol to break that cycle. 26 29 27 30 ### 1. Your Digital Home (The PDS) 28 31 ··· 48 51 49 52 --- 50 53 51 - ## Effortless Sharing Across the Network 54 + ## Effortless sharing across the network 52 55 53 56 The real advantage of an open network is how easily you can connect. In traditional encrypted 54 57 apps, you have to exchange complex "invite codes" or "public keys" just to see a single file. 55 58 56 - Since everyone on the AT Protocol already has a digital identity, sharing is built-in. 59 + Since everyone in the Atmosphere already has a digital identity, sharing is built-in. 57 60 58 - ### No More Invite Codes 61 + ### No more invite codes 59 62 60 - If your friends or colleagues are on Bluesky, they already have a handle. You don't need to 61 - ask for their "Opake address." You just type their handle (`@name.bsky.social`), and Opake 62 - handles the rest. 63 + If your friends or colleagues are on Bluesky (or any other AT Protocol app), they already have a 64 + handle. You don't need to ask for their "Opake address." You just type their handle 65 + (`@name.bsky.social`), and Opake handles the rest. 63 66 64 67 1. Opake finds their unique digital ID. 65 68 2. It identifies their public lock on the network. ··· 95 98 your digital life is no longer at the mercy of a single company's terms of service. 96 99 </Callout> 97 100 98 - Ready to learn about how we keep this open network private? Read about [Encryption & Keys](/docs/encryption-keys). 101 + Ready to learn about how we keep this open network private? Read about [Encryption & Keys](/docs/encryption).
+15 -15
apps/web/src/content/docs/cli.mdx apps/web/src/content/docs/build/cli.mdx
··· 11 11 12 12 <CodeBlock language="sh">git clone https://tangled.org/sans-self.org/opake.app</CodeBlock> 13 13 14 - <CodeBlock language="sh">cd opake.app && cargo install --path crates/opake-cli</CodeBlock> 14 + <CodeBlock language="sh">cd opake.app && cargo install --path apps/cli</CodeBlock> 15 15 16 16 This puts the `opake` binary in your `~/.cargo/bin/` directory. Pre-built binaries and crates.io publishing are planned. 17 17 ··· 23 23 24 24 ### Login 25 25 26 - <CodeBlock language="sh">opake login you.bsky.social</CodeBlock> 26 + <CodeBlock language="sh">opake account login you.bsky.social</CodeBlock> 27 27 28 28 Override the PDS or fall back to legacy app-password auth: 29 29 30 - <CodeBlock language="sh">opake login you.bsky.social --pds https://pds.example.com</CodeBlock> 30 + <CodeBlock language="sh">opake account login you.bsky.social --pds https://pds.example.com</CodeBlock> 31 31 32 - <CodeBlock language="sh">opake login you.bsky.social --legacy</CodeBlock> 32 + <CodeBlock language="sh">opake account login you.bsky.social --legacy</CodeBlock> 33 33 34 34 ### Managing Accounts 35 35 36 - <CodeBlock language="sh">opake accounts</CodeBlock> 36 + <CodeBlock language="sh">opake account list</CodeBlock> 37 37 38 - <CodeBlock language="sh">opake set-default bob.other.com</CodeBlock> 38 + <CodeBlock language="sh">opake account set-default bob.other.com</CodeBlock> 39 39 40 40 Use a specific account for a single command with `--as`: 41 41 ··· 87 87 88 88 ### Direct Sharing (Grants) 89 89 90 - <CodeBlock language="sh">opake share secret.pdf bob.bsky.social</CodeBlock> 90 + <CodeBlock language="sh">opake share new secret.pdf bob.bsky.social</CodeBlock> 91 91 92 - <CodeBlock language="sh">opake shared</CodeBlock> 92 + <CodeBlock language="sh">opake share list</CodeBlock> 93 93 94 - <CodeBlock language="sh">opake inbox</CodeBlock> 94 + <CodeBlock language="sh">opake share inbox</CodeBlock> 95 95 96 - <CodeBlock language="sh">opake revoke at://did:plc:123/app.opake.grant/tid456</CodeBlock> 96 + <CodeBlock language="sh">opake share revoke at://did:plc:123/app.opake.grant/tid456</CodeBlock> 97 97 98 - ### Group Sharing (Keyrings) 98 + ### Group Sharing (Workspaces) 99 99 100 - <CodeBlock language="sh">opake keyring create "The Collective"</CodeBlock> 100 + <CodeBlock language="sh">opake workspace create "The Collective"</CodeBlock> 101 101 102 - <CodeBlock language="sh">opake keyring add-member "The Collective" alice.bsky.social</CodeBlock> 102 + <CodeBlock language="sh">opake workspace add-member "The Collective" alice.bsky.social</CodeBlock> 103 103 104 - <CodeBlock language="sh">opake upload internal-docs.zip --keyring "The Collective"</CodeBlock> 104 + <CodeBlock language="sh">opake upload internal-docs.zip --workspace "The Collective"</CodeBlock> 105 105 106 106 --- 107 107 ··· 117 117 118 118 ### The Nuclear Option 119 119 120 - <CodeBlock language="sh">opake logout alice.bsky.social</CodeBlock> 120 + <CodeBlock language="sh">opake account logout alice.bsky.social</CodeBlock> 121 121 122 122 <CodeBlock language="sh">opake rm -r /</CodeBlock> 123 123
apps/web/src/content/docs/encryption-keys.mdx apps/web/src/content/docs/understand/encryption.mdx
+1 -1
apps/web/src/content/docs/getting-started.mdx apps/web/src/content/docs/use/getting-started.mdx
··· 60 60 image, nor what folder you've stored it in. 61 61 </Callout> 62 62 63 - Ready to dive deeper into how the math actually works? Read about [Encryption & Keys](/docs/encryption-keys). 63 + Ready to dive deeper into how the math actually works? Read about [Encryption & Keys](/docs/encryption).
+1 -1
apps/web/src/content/docs/glossary.mdx apps/web/src/content/docs/understand/glossary.mdx
··· 12 12 13 13 ### Indexer 14 14 15 - A specialized service that indexes the [Firehose](#firehose) and provides a fast way to discover which files have been shared with you. It never sees your plaintext data. Fills the atproto "indexer" protocol role, but we call it the indexer because all payloads are ciphertext and it serves no rendered views. 15 + A specialized service that indexes the [Firehose](#firehose) and provides a fast way to discover which files have been shared with you. It never sees your plaintext data. Fills the atproto "appview" protocol role, but we call it the indexer because all payloads are ciphertext and it serves no rendered views. 16 16 17 17 ### Cabinet 18 18
+76 -56
apps/web/src/content/docs/index.mdx
··· 3 3 <DocsSubtitle>Everything you need to get the most out of Opake.</DocsSubtitle> 4 4 </DocsHeader> 5 5 6 - <DocsIndexGrid> 7 - <DocsIndexCard href="/docs/getting-started" icon="sparkles"> 8 - <DocsIndexTitle>Getting Started</DocsIndexTitle> 9 - <DocsIndexBody>Set up your cabinet, create your first encrypted file, and explore the interface.</DocsIndexBody> 10 - </DocsIndexCard> 6 + <DocsIndexPrimary 7 + href="/docs/getting-started" 8 + icon="sparkles" 9 + title="Just getting started?" 10 + body="Set up your cabinet in a few minutes. No prior knowledge of atproto, encryption, or command lines required — you can learn the rest as you go." 11 + /> 11 12 12 - <DocsIndexCard href="/docs/at-protocol" icon="network"> 13 - <DocsIndexTitle>AT Protocol</DocsIndexTitle> 14 - <DocsIndexBody> 15 - The open standard powering Opake — identity, data portability, and federation. 16 - </DocsIndexBody> 17 - </DocsIndexCard> 13 + <DocsIndexSecondary 14 + href="/docs/faq" 15 + icon="question" 16 + label="Got a specific question?" 17 + body="Jump into the FAQ — privacy, security, how Opake compares to alternatives." 18 + /> 18 19 19 - <DocsIndexCard href="/docs/encryption-keys" icon="lock"> 20 - <DocsIndexTitle>Encryption & Keys</DocsIndexTitle> 21 - <DocsIndexBody> 22 - How end-to-end encryption works in Opake and how your keys are managed. 23 - </DocsIndexBody> 24 - </DocsIndexCard> 20 + <DocsIndexSection 21 + label="For users" 22 + description="Store, share, and recover files with Opake." 23 + > 24 + <DocsIndexGrid> 25 + <DocsIndexCard href="/docs/getting-started" icon="sparkles"> 26 + <DocsIndexTitle>Getting Started</DocsIndexTitle> 27 + <DocsIndexBody>Set up your cabinet, create your first encrypted file, and explore the interface.</DocsIndexBody> 28 + </DocsIndexCard> 25 29 26 - <DocsIndexCard href="/docs/sharing-dids" icon="share"> 27 - <DocsIndexTitle>Sharing & DIDs</DocsIndexTitle> 28 - <DocsIndexBody> 29 - Share files using decentralised identifiers without a central authority. 30 - </DocsIndexBody> 31 - </DocsIndexCard> 30 + <DocsIndexCard href="/docs/sharing" icon="share"> 31 + <DocsIndexTitle>Sharing</DocsIndexTitle> 32 + <DocsIndexBody>Share a file with one other person — no accounts, no invites, just their handle.</DocsIndexBody> 33 + </DocsIndexCard> 32 34 33 - <DocsIndexCard href="/docs/keyrings" icon="group"> 34 - <DocsIndexTitle>Keyrings & Groups</DocsIndexTitle> 35 - <DocsIndexBody> 36 - Manage secure group sharing for families, teams, and research groups. 37 - </DocsIndexBody> 38 - </DocsIndexCard> 35 + <DocsIndexCard href="/docs/workspaces" icon="group"> 36 + <DocsIndexTitle>Workspaces</DocsIndexTitle> 37 + <DocsIndexBody>Share folders with teams, families, or research groups without re-encrypting each file.</DocsIndexBody> 38 + </DocsIndexCard> 39 39 40 - <DocsIndexCard href="/docs/cli" icon="terminal"> 41 - <DocsIndexTitle>The CLI Manual</DocsIndexTitle> 42 - <DocsIndexBody> 43 - Command-line reference for managing your vault, grants, and identity. 44 - </DocsIndexBody> 45 - </DocsIndexCard> 40 + <DocsIndexCard href="/docs/pairing" icon="pairing"> 41 + <DocsIndexTitle>Multi-Device</DocsIndexTitle> 42 + <DocsIndexBody>Move your identity to a new phone or laptop without exposing it to the network.</DocsIndexBody> 43 + </DocsIndexCard> 46 44 47 - <DocsIndexCard href="/docs/seed-phrase" icon="seedling"> 48 - <DocsIndexTitle>Your Seed Phrase</DocsIndexTitle> 49 - <DocsIndexBody>Back up and recover your identity with a 24-word recovery phrase.</DocsIndexBody> 50 - </DocsIndexCard> 45 + <DocsIndexCard href="/docs/seed-phrase" icon="seedling"> 46 + <DocsIndexTitle>Your Seed Phrase</DocsIndexTitle> 47 + <DocsIndexBody>Back up your identity with a 24-word phrase — the fallback when all else fails.</DocsIndexBody> 48 + </DocsIndexCard> 51 49 52 - <DocsIndexCard href="/docs/pairing" icon="pairing"> 53 - <DocsIndexTitle>Multi-Device Magic</DocsIndexTitle> 54 - <DocsIndexBody> 55 - Securely transfer your identity keypair to new devices using your PDS as a relay. 56 - </DocsIndexBody> 57 - </DocsIndexCard> 50 + <DocsIndexCard href="/docs/troubleshooting" icon="question"> 51 + <DocsIndexTitle>Troubleshooting</DocsIndexTitle> 52 + <DocsIndexBody>Common problems and the quickest way through them.</DocsIndexBody> 53 + </DocsIndexCard> 54 + </DocsIndexGrid> 55 + </DocsIndexSection> 58 56 59 - <DocsIndexCard href="/docs/glossary" icon="book"> 60 - <DocsIndexTitle>Glossary</DocsIndexTitle> 61 - <DocsIndexBody> 62 - A quick-hit reference for the terminology and acronyms we use in Opake. 63 - </DocsIndexBody> 64 - </DocsIndexCard> 57 + <DocsIndexSection 58 + label="Under the hood" 59 + description="How Opake protects your data — the crypto, the records, the protocol. Written for anyone curious enough to look, not just developers." 60 + > 61 + <DocsIndexGrid> 62 + <DocsIndexCard href="/docs/encryption" icon="lock"> 63 + <DocsIndexTitle>Encryption & Keys</DocsIndexTitle> 64 + <DocsIndexBody>How end-to-end encryption works in Opake and how your keys are managed.</DocsIndexBody> 65 + </DocsIndexCard> 65 66 66 - <DocsIndexCard href="/faq" icon="question"> 67 - <DocsIndexTitle>FAQ</DocsIndexTitle> 68 - <DocsIndexBody>Common questions about privacy, security, and how Opake compares to alternatives.</DocsIndexBody> 69 - </DocsIndexCard> 70 - </DocsIndexGrid> 67 + <DocsIndexCard href="/docs/at-protocol" icon="network"> 68 + <DocsIndexTitle>AT Protocol</DocsIndexTitle> 69 + <DocsIndexBody>The open standard powering Opake — identity, data portability, and federation.</DocsIndexBody> 70 + </DocsIndexCard> 71 + 72 + <DocsIndexCard href="/docs/glossary" icon="book"> 73 + <DocsIndexTitle>Glossary</DocsIndexTitle> 74 + <DocsIndexBody>A quick-hit reference for the terminology and acronyms we use in Opake.</DocsIndexBody> 75 + </DocsIndexCard> 76 + </DocsIndexGrid> 77 + </DocsIndexSection> 78 + 79 + <DocsIndexSection 80 + label="For developers" 81 + description="Program against Opake. The CLI is the reference implementation; the SDK and React bindings wrap the same primitives for building your own clients." 82 + > 83 + <DocsIndexGrid> 84 + <DocsIndexCard href="/docs/cli" icon="terminal"> 85 + <DocsIndexTitle>The CLI Manual</DocsIndexTitle> 86 + <DocsIndexBody>Complete command reference for the Opake CLI — identity, files, sharing, and more.</DocsIndexBody> 87 + </DocsIndexCard> 88 + </DocsIndexGrid> 89 + </DocsIndexSection> 90 +
+4 -4
apps/web/src/content/docs/keyrings.mdx apps/web/src/content/docs/use/workspaces.mdx
··· 38 38 39 39 </PlatformTab> 40 40 <PlatformTab name="CLI"> 41 - <CodeBlock language="sh">opake keyring create "Family Photos"</CodeBlock> 41 + <CodeBlock language="sh">opake workspace create "Family Photos"</CodeBlock> 42 42 </PlatformTab> 43 43 </PlatformToggle> 44 44 ··· 61 61 3. It keeps a history of the old keys so that older files can still be decrypted by the remaining group. 62 62 63 63 <Callout type="warning"> 64 - **Forward Secrecy Reality:** Just like with [Grants](/docs/sharing-dids), removing someone from a 64 + **Forward Secrecy Reality:** Just like with [Grants](/docs/sharing), removing someone from a 65 65 Keyring prevents them from accessing *future* files. If they already downloaded and decrypted old 66 66 files, those files remain in their possession. 67 67 </Callout> ··· 72 72 73 73 When you upload a file, you can choose to associate it with a Keyring instead of an individual person. 74 74 75 - <CodeBlock language="sh">opake upload "budget.pdf" --keyring "Family Photos"</CodeBlock> 75 + <CodeBlock language="sh">opake upload "budget.pdf" --workspace "Family Photos"</CodeBlock> 76 76 77 - Every member of that Keyring will see the file in their [Inbox](/docs/sharing-dids) and can decrypt it instantly. 77 + Every member of that Keyring will see the file in their [Inbox](/docs/sharing) and can decrypt it instantly. 78 78 79 79 Ready for a quick reference on all these terms? Check the [Glossary](/docs/glossary).
apps/web/src/content/docs/pairing.mdx apps/web/src/content/docs/use/pairing.mdx
apps/web/src/content/docs/seed-phrase.mdx apps/web/src/content/docs/use/seed-phrase.mdx
apps/web/src/content/docs/sharing-dids.mdx apps/web/src/content/docs/use/sharing.mdx
+2 -2
apps/web/src/content/faq.mdx apps/web/src/content/docs/faq.mdx
··· 26 26 </FaqItem> 27 27 28 28 <FaqItem question="Can I share files with people who don't use Opake?"> 29 - Because of how our [Encryption & Keys](/docs/encryption-keys) work, the recipient *must* have an 30 - AT Protocol identity (a DID) to receive a secure sharing [Grant](/docs/sharing-dids). This is a 29 + Because of how our [Encryption & Keys](/docs/encryption) work, the recipient *must* have an 30 + AT Protocol identity (a DID) to receive a secure sharing [Grant](/docs/sharing). This is a 31 31 feature, not a bug. It ensures that the file key is wrapped specifically to their public key. If 32 32 you need to share with a total outsider, you'll need to help them join the network first—it's 33 33 worth it for the privacy.
+1 -1
apps/web/src/content/troubleshooting.mdx apps/web/src/content/docs/use/troubleshooting.mdx
··· 54 54 If you can't share a file with someone: 55 55 56 56 - **Handle vs. DID:** Ensure the handle is correct. 57 - - **Public Key Missing:** The recipient must have logged into Opake at least once to publish their [Public Encryption Key](/docs/encryption-keys). If they haven't done this, you cannot "wrap" a file to them. 57 + - **Public Key Missing:** The recipient must have logged into Opake at least once to publish their [Public Encryption Key](/docs/encryption). If they haven't done this, you cannot "wrap" a file to them. 58 58 59 59 ### "I don't see shared files in my inbox" 60 60
+97 -28
apps/web/src/lib/docs-registry.ts
··· 1 1 import type { IconName } from "@/components/content/icons"; 2 2 3 + /** 4 + * Documentation audiences. The folder names on disk match these keys; the 5 + * human-facing labels live in `CATEGORY_META` so we can rename the card 6 + * headings without moving files. 7 + */ 8 + export type DocCategory = "use" | "understand" | "build"; 9 + 3 10 export interface DocMeta { 4 11 readonly slug: string; 12 + readonly category: DocCategory; 5 13 readonly title: string; 6 14 readonly description: string; 7 15 readonly icon: IconName; 8 16 } 9 17 18 + export interface CategoryMeta { 19 + readonly key: DocCategory; 20 + readonly label: string; 21 + readonly description: string; 22 + } 23 + 24 + /** 25 + * Audience metadata rendered on the docs landing and sidebar. "Under the 26 + * hood" intentionally breaks the "For X" pattern — the section covers the 27 + * crypto + protocol model, which anyone with curiosity can read regardless 28 + * of whether they'd self-identify as a developer. 29 + */ 30 + export const CATEGORY_META: readonly CategoryMeta[] = [ 31 + { 32 + key: "use", 33 + label: "For users", 34 + description: "Store, share, and recover files with Opake.", 35 + }, 36 + { 37 + key: "understand", 38 + label: "Under the hood", 39 + description: "How Opake protects your data — the crypto, the records, the protocol.", 40 + }, 41 + { 42 + key: "build", 43 + label: "For developers", 44 + description: "Program against Opake: CLI, SDK, React hooks, lexicons.", 45 + }, 46 + ]; 47 + 10 48 /** 11 49 * Single source of truth for documentation section metadata. 12 50 * Used by both public docs routes and cabinet docs routes. 13 51 */ 14 52 export const DOCS_REGISTRY: readonly DocMeta[] = [ 53 + // -- For users ------------------------------------------------------------- 15 54 { 16 55 slug: "getting-started", 56 + category: "use", 17 57 title: "Getting Started", 18 58 icon: "sparkles", 19 59 description: 20 60 "Set up your cabinet, create your first encrypted file, and explore the interface.", 21 61 }, 22 62 { 23 - slug: "at-protocol", 24 - title: "AT Protocol", 25 - icon: "network", 26 - description: "The open standard powering Opake — identity, data portability, and federation.", 63 + slug: "pairing", 64 + category: "use", 65 + title: "Multi-Device Magic", 66 + icon: "pairing", 67 + description: 68 + "Securely transfer your identity keypair to new devices using your PDS as a relay.", 27 69 }, 28 70 { 29 - slug: "encryption-keys", 30 - title: "Encryption & Keys", 31 - icon: "lock", 32 - description: "How end-to-end encryption works in Opake and how your keys are managed.", 71 + slug: "seed-phrase", 72 + category: "use", 73 + title: "Your Seed Phrase", 74 + icon: "seedling", 75 + description: "Back up and recover your identity with a 24-word recovery phrase.", 33 76 }, 34 77 { 35 - slug: "sharing-dids", 36 - title: "Sharing & DIDs", 78 + slug: "sharing", 79 + category: "use", 80 + title: "Sharing", 37 81 icon: "share", 38 - description: "Share files using decentralised identifiers without a central authority.", 82 + description: "Share files with another person — one-to-one grants and recipient discovery.", 39 83 }, 40 84 { 41 - slug: "keyrings", 42 - title: "Keyrings & Groups", 85 + slug: "workspaces", 86 + category: "use", 87 + title: "Workspaces", 43 88 icon: "group", 44 - description: "Manage secure group sharing for families, teams, and research groups.", 89 + description: "Share folders with teams, families, and research groups — no re-encryption.", 45 90 }, 46 91 { 47 - slug: "seed-phrase", 48 - title: "Your Seed Phrase", 49 - icon: "seedling", 50 - description: "Back up and recover your identity with a 24-word recovery phrase.", 92 + slug: "troubleshooting", 93 + category: "use", 94 + title: "Troubleshooting", 95 + icon: "question", 96 + description: "Common problems and how to fix them.", 51 97 }, 98 + 99 + // -- Under the hood -------------------------------------------------------- 52 100 { 53 - slug: "pairing", 54 - title: "Multi-Device Magic", 55 - icon: "pairing", 56 - description: 57 - "Securely transfer your identity keypair to new devices using your PDS as a relay.", 101 + slug: "encryption", 102 + category: "understand", 103 + title: "Encryption & Keys", 104 + icon: "lock", 105 + description: "How end-to-end encryption works in Opake and how your keys are managed.", 58 106 }, 59 107 { 60 - slug: "cli", 61 - title: "The CLI Manual", 62 - icon: "terminal", 63 - description: 64 - "Complete command reference for the Opake CLI — identity, files, sharing, and more.", 108 + slug: "at-protocol", 109 + category: "understand", 110 + title: "AT Protocol", 111 + icon: "network", 112 + description: "The open standard powering Opake — identity, data portability, and federation.", 65 113 }, 66 114 { 67 115 slug: "glossary", 116 + category: "understand", 68 117 title: "Glossary", 69 118 icon: "book", 70 119 description: "A quick-hit reference for the terminology and acronyms we use in Opake.", 71 120 }, 121 + 122 + // -- For developers -------------------------------------------------------- 123 + { 124 + slug: "cli", 125 + category: "build", 126 + title: "The CLI Manual", 127 + icon: "terminal", 128 + description: 129 + "Complete command reference for the Opake CLI — identity, files, sharing, and more.", 130 + }, 131 + 132 + // -- Cross-cutting --------------------------------------------------------- 72 133 { 73 134 slug: "faq", 135 + // FAQ sits outside a category intentionally; `category: "use"` keeps the 136 + // type simple while rendering code can special-case the slug to pin it 137 + // to the top level of the sidebar / landing. 138 + category: "use", 74 139 title: "FAQ", 75 140 icon: "question", 76 141 description: ··· 81 146 export function findDoc(slug: string): DocMeta | undefined { 82 147 return DOCS_REGISTRY.find((d) => d.slug === slug); 83 148 } 149 + 150 + export function docsByCategory(category: DocCategory): readonly DocMeta[] { 151 + return DOCS_REGISTRY.filter((d) => d.category === category && d.slug !== "faq"); 152 + }
+18 -16
apps/web/src/routes/_public/docs/$slug.tsx
··· 4 4 import { findDoc } from "@/lib/docs-registry"; 5 5 import { ogMeta } from "@/lib/og-meta"; 6 6 7 - import GettingStarted from "@/content/docs/getting-started.mdx"; 8 - import AtProtocol from "@/content/docs/at-protocol.mdx"; 9 - import EncryptionKeys from "@/content/docs/encryption-keys.mdx"; 10 - import SharingDids from "@/content/docs/sharing-dids.mdx"; 11 - import Keyrings from "@/content/docs/keyrings.mdx"; 12 - import SeedPhrase from "@/content/docs/seed-phrase.mdx"; 13 - import Pairing from "@/content/docs/pairing.mdx"; 14 - import Cli from "@/content/docs/cli.mdx"; 15 - import Glossary from "@/content/docs/glossary.mdx"; 16 - import Faq from "@/content/faq.mdx"; 7 + import GettingStarted from "@/content/docs/use/getting-started.mdx"; 8 + import Pairing from "@/content/docs/use/pairing.mdx"; 9 + import SeedPhrase from "@/content/docs/use/seed-phrase.mdx"; 10 + import Sharing from "@/content/docs/use/sharing.mdx"; 11 + import Workspaces from "@/content/docs/use/workspaces.mdx"; 12 + import Troubleshooting from "@/content/docs/use/troubleshooting.mdx"; 13 + import Encryption from "@/content/docs/understand/encryption.mdx"; 14 + import AtProtocol from "@/content/docs/understand/at-protocol.mdx"; 15 + import Glossary from "@/content/docs/understand/glossary.mdx"; 16 + import Cli from "@/content/docs/build/cli.mdx"; 17 + import Faq from "@/content/docs/faq.mdx"; 17 18 18 19 const CONTENT_BY_SLUG: Partial< 19 20 Record<string, ComponentType<{ readonly components?: Record<string, ComponentType<never>> }>> 20 21 > = { 21 22 "getting-started": GettingStarted, 23 + pairing: Pairing, 24 + "seed-phrase": SeedPhrase, 25 + sharing: Sharing, 26 + workspaces: Workspaces, 27 + troubleshooting: Troubleshooting, 28 + encryption: Encryption, 22 29 "at-protocol": AtProtocol, 23 - "encryption-keys": EncryptionKeys, 24 - "sharing-dids": SharingDids, 25 - keyrings: Keyrings, 26 - "seed-phrase": SeedPhrase, 27 - pairing: Pairing, 28 - cli: Cli, 29 30 glossary: Glossary, 31 + cli: Cli, 30 32 faq: Faq, 31 33 }; 32 34
+1 -1
apps/web/src/routes/_public/faq.tsx
··· 1 1 import { createFileRoute } from "@tanstack/react-router"; 2 2 import { ogMeta } from "@/lib/og-meta"; 3 3 import { MdxContent } from "@/components/content/MdxProvider"; 4 - import FaqContent from "@/content/faq.mdx"; 4 + import FaqContent from "@/content/docs/faq.mdx"; 5 5 6 6 function FaqPage() { 7 7 return (
+1 -1
apps/web/src/routes/_public/troubleshooting.tsx
··· 1 1 import { createFileRoute } from "@tanstack/react-router"; 2 2 import { ogMeta } from "@/lib/og-meta"; 3 3 import { MdxContent } from "@/components/content/MdxProvider"; 4 - import TroubleshootingContent from "@/content/troubleshooting.mdx"; 4 + import TroubleshootingContent from "@/content/docs/use/troubleshooting.mdx"; 5 5 6 6 function TroubleshootingPage() { 7 7 return (
+18 -16
apps/web/src/routes/cabinet/docs/$slug.lazy.tsx
··· 5 5 import { MdxContent } from "@/components/content/MdxProvider"; 6 6 import { findDoc } from "@/lib/docs-registry"; 7 7 8 - import GettingStarted from "@/content/docs/getting-started.mdx"; 9 - import AtProtocol from "@/content/docs/at-protocol.mdx"; 10 - import EncryptionKeys from "@/content/docs/encryption-keys.mdx"; 11 - import SharingDids from "@/content/docs/sharing-dids.mdx"; 12 - import Keyrings from "@/content/docs/keyrings.mdx"; 13 - import SeedPhrase from "@/content/docs/seed-phrase.mdx"; 14 - import Pairing from "@/content/docs/pairing.mdx"; 15 - import Cli from "@/content/docs/cli.mdx"; 16 - import Glossary from "@/content/docs/glossary.mdx"; 17 - import Faq from "@/content/faq.mdx"; 8 + import GettingStarted from "@/content/docs/use/getting-started.mdx"; 9 + import Pairing from "@/content/docs/use/pairing.mdx"; 10 + import SeedPhrase from "@/content/docs/use/seed-phrase.mdx"; 11 + import Sharing from "@/content/docs/use/sharing.mdx"; 12 + import Workspaces from "@/content/docs/use/workspaces.mdx"; 13 + import Troubleshooting from "@/content/docs/use/troubleshooting.mdx"; 14 + import Encryption from "@/content/docs/understand/encryption.mdx"; 15 + import AtProtocol from "@/content/docs/understand/at-protocol.mdx"; 16 + import Glossary from "@/content/docs/understand/glossary.mdx"; 17 + import Cli from "@/content/docs/build/cli.mdx"; 18 + import Faq from "@/content/docs/faq.mdx"; 18 19 19 20 type MdxComponent = ComponentType<{ 20 21 readonly components?: Record<string, ComponentType<never>>; ··· 22 23 23 24 const CONTENT_BY_SLUG: Partial<Record<string, MdxComponent>> = { 24 25 "getting-started": GettingStarted, 26 + pairing: Pairing, 27 + "seed-phrase": SeedPhrase, 28 + sharing: Sharing, 29 + workspaces: Workspaces, 30 + troubleshooting: Troubleshooting, 31 + encryption: Encryption, 25 32 "at-protocol": AtProtocol, 26 - "encryption-keys": EncryptionKeys, 27 - "sharing-dids": SharingDids, 28 - keyrings: Keyrings, 29 - "seed-phrase": SeedPhrase, 30 - pairing: Pairing, 31 - cli: Cli, 32 33 glossary: Glossary, 34 + cli: Cli, 33 35 faq: Faq, 34 36 }; 35 37
+78 -25
apps/web/src/routes/cabinet/docs/index.lazy.tsx
··· 1 1 import { createElement } from "react"; 2 2 import { createLazyFileRoute, Link } from "@tanstack/react-router"; 3 - import { ArrowSquareOutIcon } from "@phosphor-icons/react"; 3 + import { ArrowSquareOutIcon, QuestionIcon, SparkleIcon } from "@phosphor-icons/react"; 4 4 import { PanelShell } from "@/components/cabinet/PanelShell"; 5 5 import { resolveIcon } from "@/components/content/icons"; 6 - import { DOCS_REGISTRY } from "@/lib/docs-registry"; 6 + import { CATEGORY_META, docsByCategory, findDoc, type DocMeta } from "@/lib/docs-registry"; 7 + 8 + function DocLink({ doc }: { readonly doc: DocMeta }) { 9 + return ( 10 + <Link 11 + to="/cabinet/docs/$slug" 12 + params={{ slug: doc.slug }} 13 + className="card card-bordered border-base-300/50 bg-base-100 hover:shadow-panel-sm flex cursor-pointer flex-row items-start gap-3 p-3.5 transition-shadow" 14 + > 15 + <div className="bg-accent flex size-8 shrink-0 items-center justify-center rounded-lg"> 16 + {createElement(resolveIcon(doc.icon), { size: 14, className: "text-primary" })} 17 + </div> 18 + <div className="flex-1"> 19 + <div className="text-ui text-base-content mb-0.5 font-medium">{doc.title}</div> 20 + <div className="text-caption text-text-muted leading-relaxed">{doc.description}</div> 21 + </div> 22 + <ArrowSquareOutIcon size={12} className="text-text-faint mt-0.5 shrink-0" /> 23 + </Link> 24 + ); 25 + } 7 26 8 27 function DocsIndexPage() { 9 28 const breadcrumbs = ( ··· 16 35 </div> 17 36 ); 18 37 38 + const faq = findDoc("faq"); 39 + 19 40 return ( 20 41 <PanelShell depth={1} breadcrumbs={breadcrumbs} footer="Documentation · Opake"> 21 - <div className="p-5"> 22 - <div className="mb-5"> 23 - <div className="text-ui text-base-content mb-1 font-medium">Documentation</div> 24 - <div className="text-text-muted text-xs"> 25 - Everything you need to get the most out of Opake. 42 + <div className="space-y-6 p-5"> 43 + {/* Primary CTA — catches visitors who don't yet know which audience fits */} 44 + <Link 45 + to="/cabinet/docs/$slug" 46 + params={{ slug: "getting-started" }} 47 + className="border-primary/40 bg-base-100 group hover:border-primary/70 hover:shadow-panel-sm flex items-center gap-4 rounded-xl border-2 p-4 transition-all" 48 + > 49 + <div className="bg-primary/15 flex size-10 shrink-0 items-center justify-center rounded-lg"> 50 + <SparkleIcon size={18} className="text-primary" /> 26 51 </div> 27 - </div> 28 - <div className="flex flex-col gap-2"> 29 - {DOCS_REGISTRY.map((s) => ( 30 - <Link 31 - key={s.slug} 32 - to="/cabinet/docs/$slug" 33 - params={{ slug: s.slug }} 34 - className="card card-bordered border-base-300/50 bg-base-100 hover:shadow-panel-sm cursor-pointer p-3.5 transition-shadow" 35 - > 36 - <div className="bg-accent flex size-8 shrink-0 items-center justify-center rounded-lg"> 37 - {createElement(resolveIcon(s.icon), { size: 14, className: "text-primary" })} 52 + <div className="flex-1"> 53 + <div className="text-ui text-base-content mb-0.5 font-medium"> 54 + Just getting started? 55 + </div> 56 + <div className="text-caption text-text-muted leading-relaxed"> 57 + Set up your cabinet in a few minutes — no prior knowledge required. 58 + </div> 59 + </div> 60 + <ArrowSquareOutIcon size={12} className="text-text-faint shrink-0" /> 61 + </Link> 62 + 63 + {/* Secondary CTA — for visitors with a specific question rather than a task */} 64 + {faq && ( 65 + <Link 66 + to="/cabinet/docs/$slug" 67 + params={{ slug: faq.slug }} 68 + className="border-border-accent/40 bg-base-100/60 group hover:border-primary/60 hover:bg-base-100 flex items-center gap-3 rounded-xl border p-3 transition-all" 69 + > 70 + <div className="bg-accent/60 flex size-8 shrink-0 items-center justify-center rounded-lg"> 71 + <QuestionIcon size={15} className="text-primary" /> 72 + </div> 73 + <div className="flex-1"> 74 + <span className="text-base-content text-ui font-medium"> 75 + Got a specific question? 76 + </span> 77 + <span className="text-text-muted text-ui ml-1.5">Jump into the FAQ.</span> 78 + </div> 79 + <ArrowSquareOutIcon size={12} className="text-text-faint shrink-0" /> 80 + </Link> 81 + )} 82 + 83 + {CATEGORY_META.map((cat) => { 84 + const docs = docsByCategory(cat.key); 85 + if (docs.length === 0) return null; 86 + return ( 87 + <section key={cat.key}> 88 + <div className="mb-2"> 89 + <div className="text-ui text-base-content font-medium">{cat.label}</div> 90 + <div className="text-caption text-text-muted">{cat.description}</div> 38 91 </div> 39 - <div className="flex-1"> 40 - <div className="text-ui text-base-content mb-0.5 font-medium">{s.title}</div> 41 - <div className="text-caption text-text-muted leading-relaxed">{s.description}</div> 92 + <div className="flex flex-col gap-2"> 93 + {docs.map((d) => ( 94 + <DocLink key={d.slug} doc={d} /> 95 + ))} 42 96 </div> 43 - <ArrowSquareOutIcon size={12} className="text-text-faint mt-0.5 shrink-0" /> 44 - </Link> 45 - ))} 46 - </div> 97 + </section> 98 + ); 99 + })} 47 100 </div> 48 101 </PanelShell> 49 102 );