Skip to content

Commit 7edd51c

Browse files
committed
Did drive
1 parent 9f61718 commit 7edd51c

36 files changed

Lines changed: 448 additions & 39 deletions

DIDS-DRIVES-ROUTING.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# DID-Native Drives and Domain Routing Plan
2+
3+
## Overview
4+
Atomic Data is moving away from a fixed "main drive" at the root `/` of a server. Instead, every Drive is a first-class decentralized citizen identified by a DID, and human-readable access is handled via a **Domain-to-DID mapping** system.
5+
6+
## Core Concepts
7+
8+
### 1. Identity vs. Alias
9+
- **Identity (Immutable)**: A Drive is identified by its DID: `did:ad:{genesis}?drive={hash}`.
10+
- **Alias (Mutable)**: A Domain or Subdomain (e.g., `joep.atomicdata.dev`, `localhost:9883`) is an alias that routes to a specific Drive DID.
11+
12+
### 2. Agent-First Onboarding
13+
Onboarding no longer starts with a "nameless" drive. It starts with the user:
14+
1. **Create Agent**: Generate an Ed25519 keypair (`did:ad:agent:...`).
15+
2. **Create Drive**: The Agent signs the genesis commit for a new Drive.
16+
3. **Bind Alias**: The Server maps the current `Host` (e.g., `localhost`) to this Drive's DID.
17+
18+
### 3. Multi-Tenancy & Routing
19+
The server uses the HTTP `Host` header to determine which Drive to serve:
20+
- `joep.atomicdata.dev` -> Serves Joep's Drive.
21+
- `jane.atomicdata.dev` -> Serves Jane's Drive.
22+
- `localhost:9883` -> Serves the developer's default Drive.
23+
24+
---
25+
26+
## Implementation Phases
27+
28+
### Phase 1: Backend Routing & Mapping
29+
- [ ] **Mapping Store**: Implement a mechanism in `atomic-server` to store and query `Host -> Drive DID` mappings.
30+
- [ ] **Host-Based Resolution**: Update the HTTP request handler to check the `Host` header.
31+
- [ ] **Relative Pathing**: Ensure that paths like `/classes` are correctly resolved relative to the Drive DID mapped to the current Host.
32+
33+
### Phase 2: Drive Lifecycle & Population
34+
- [ ] **Standard Initialization**: Refactor `atomic_lib::populate` so it can be applied to any Drive DID (creating `/classes`, `/tags`, etc. within that drive).
35+
- [ ] **Drive Resource**: Ensure the Drive resource itself contains the necessary metadata (Drive Public Key, Hash, Owner).
36+
37+
### Phase 3: Server Setup & Onboarding UI
38+
- [ ] **Node Setup State**: Detect when a server has no mappings and enter "Setup Mode".
39+
- [ ] **Setup UI**: A dedicated frontend flow for creating an Agent and the first Drive.
40+
- [ ] **Alias Registration**: A secure way for the first Agent to "claim" the server's primary domain.
41+
42+
### Phase 4: Decentralized Discovery
43+
- [ ] **JSON-AD Headers**: Include the true `did:ad` identity in HTTP responses so clients can switch to P2P resolution.
44+
- [ ] **DHT/Reticulum Announces**: Servers announce the Drive hashes they host to the P2P networks.
45+
46+
---
47+
48+
## Success Criteria
49+
1. Running `atomic-server` for the first time leads to an Agent/Drive creation flow.
50+
2. Multiple drives can be accessed via subdomains on the same server instance.
51+
3. Every drive has its own `/classes`, `/templates`, and `/files` collections.
52+
4. Resources shared via `did:ad` identifiers are resolvable across different servers using the routing hints.

browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useSettings } from '../../../../../helpers/AppSettings';
2525
const DRIVE_PUBLIC_KEY = 'https://atomicdata.dev/properties/drive/publicKey';
2626
const DRIVE_PRIVATE_KEY = 'https://atomicdata.dev/properties/drive/privateKey';
2727
const DRIVE_HASH = 'https://atomicdata.dev/properties/drive/hash';
28+
const SUBDOMAIN = 'https://atomicdata.dev/properties/subdomain';
2829

2930
function decodeBase64(base64: string): Uint8Array {
3031
const binary = atob(base64);
@@ -50,8 +51,17 @@ export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
5051
}) => {
5152
const store = useStore();
5253
const nameFieldId = useId();
54+
const subdomainFieldId = useId();
5355
const { setDrive } = useSettings();
5456
const [name, setName] = useState('');
57+
const [subdomain, setSubdomain] = useState('');
58+
const [subdomainEdited, setSubdomainEdited] = useState(false);
59+
60+
useEffect(() => {
61+
if (!subdomainEdited) {
62+
setSubdomain(stringToSlug(name));
63+
}
64+
}, [name, subdomainEdited]);
5565

5666
const createAndNavigate = useCreateAndNavigate();
5767

@@ -86,6 +96,7 @@ export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
8696
[DRIVE_PUBLIC_KEY]: driveKeys.publicKey,
8797
[DRIVE_PRIVATE_KEY]: driveKeys.privateKey,
8898
[DRIVE_HASH]: driveHash,
99+
[SUBDOMAIN]: subdomain.trim(),
89100
},
90101
{
91102
noParent: true,
@@ -138,6 +149,7 @@ export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
138149
onClose();
139150
}, [
140151
name,
152+
subdomain,
141153
createAndNavigate,
142154
onClose,
143155
setDrive,
@@ -175,6 +187,19 @@ export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
175187
/>
176188
</InputWrapper>
177189
</Field>
190+
<Field label='Subdomain' fieldId={subdomainFieldId}>
191+
<InputWrapper>
192+
<InputStyled
193+
id={subdomainFieldId}
194+
placeholder='my-drive'
195+
value={subdomain}
196+
onChange={e => {
197+
setSubdomain(e.target.value);
198+
setSubdomainEdited(true);
199+
}}
200+
/>
201+
</InputWrapper>
202+
</Field>
178203
</form>
179204
</DialogContent>
180205
<DialogActions>

browser/data-browser/src/locales/de.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3465,3 +3465,7 @@ msgid ""
34653465
"Save it somewhere safe, the secret will not be show again and if\n"
34663466
"you lose it you will not be able to access this user again."
34673467
msgstr ""
3468+
3469+
#: src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
3470+
msgid "Subdomain"
3471+
msgstr ""

browser/data-browser/src/locales/en.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3487,3 +3487,7 @@ msgstr ""
34873487
"IMPORTANT! Below is your agent secret, you use this to login.\n"
34883488
"Save it somewhere safe, the secret will not be show again and if\n"
34893489
"you lose it you will not be able to access this user again."
3490+
3491+
#: src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
3492+
msgid "Subdomain"
3493+
msgstr "Subdomain"

browser/data-browser/src/locales/es.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3451,3 +3451,7 @@ msgid ""
34513451
"Save it somewhere safe, the secret will not be show again and if\n"
34523452
"you lose it you will not be able to access this user again."
34533453
msgstr ""
3454+
3455+
#: src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
3456+
msgid "Subdomain"
3457+
msgstr ""

browser/data-browser/src/locales/fr.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,3 +3464,7 @@ msgid ""
34643464
"Save it somewhere safe, the secret will not be show again and if\n"
34653465
"you lose it you will not be able to access this user again."
34663466
msgstr ""
3467+
3468+
#: src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
3469+
msgid "Subdomain"
3470+
msgstr ""

home.png

-18.5 KB
Binary file not shown.

home2.png

-340 KB
Binary file not shown.

home3.png

-18.5 KB
Binary file not shown.

lib/src/commit.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ impl Commit {
307307
let subject_url = match &subject {
308308
Subject::Internal(u) => u.clone(),
309309
Subject::External(u) => u.clone(),
310-
Subject::Did(u) => u.clone(),
310+
Subject::Did { url, .. } => url.clone(),
311311
};
312312

313313
if subject_url.query().is_some() {
@@ -360,7 +360,7 @@ impl Commit {
360360
// For new DID resources, grant the signer explicit write access so future
361361
// commits don't need drive-level rights. Agents are excluded because they
362362
// already have self-write via their subject matching the agent check.
363-
if matches!(applied.resource_new.get_subject(), Subject::Did(_)) {
363+
if matches!(applied.resource_new.get_subject(), Subject::Did { .. }) {
364364
let is_agent = applied
365365
.resource_new
366366
.get(urls::IS_A)

0 commit comments

Comments
 (0)