Skip to content

Commit 6cb2a1a

Browse files
committed
migrate to dids, dht resolve #1146
1 parent d52f451 commit 6cb2a1a

35 files changed

Lines changed: 917 additions & 278 deletions

.claude/settings.local.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"mcp__chrome-devtools__new_page",
5+
"mcp__chrome-devtools__take_screenshot",
6+
"Bash(cargo test --package atomic-server --test dht_resolve --no-run)",
7+
"Bash(cargo test --package atomic-server --test dht_resolve -- --nocapture)"
8+
]
9+
}
10+
}

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { core, useStore, server, dataBrowser } from '@tomic/react';
1+
import {
2+
core,
3+
useStore,
4+
server,
5+
dataBrowser,
6+
generateKeyPair,
7+
} from '@tomic/react';
28
import { useState, useCallback, FormEvent, FC, useEffect, useId } from 'react';
39
import { styled } from 'styled-components';
410
import { stringToSlug } from '../../../../../helpers/stringToSlug';
@@ -16,6 +22,27 @@ import { CustomResourceDialogProps } from '../../useNewResourceUI';
1622
import { useCreateAndNavigate } from '../../../../../hooks/useCreateAndNavigate';
1723
import { useSettings } from '../../../../../helpers/AppSettings';
1824

25+
const DRIVE_PUBLIC_KEY = 'https://atomicdata.dev/properties/drive/publicKey';
26+
const DRIVE_PRIVATE_KEY = 'https://atomicdata.dev/properties/drive/privateKey';
27+
const DRIVE_HASH = 'https://atomicdata.dev/properties/drive/hash';
28+
29+
function decodeBase64(base64: string): Uint8Array {
30+
const binary = atob(base64);
31+
const bytes = new Uint8Array(binary.length);
32+
33+
for (let i = 0; i < binary.length; i++) {
34+
bytes[i] = binary.charCodeAt(i);
35+
}
36+
37+
return bytes;
38+
}
39+
40+
function toHex(bytes: Uint8Array): string {
41+
return Array.from(bytes)
42+
.map(b => b.toString(16).padStart(2, '0'))
43+
.join('');
44+
}
45+
1946
export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
2047
onClose,
2148
onCreated,
@@ -38,27 +65,44 @@ export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
3865
'No agent set in the Store, required when creating a Drive',
3966
);
4067
}
68+
const driveKeys = await generateKeyPair();
69+
const drivePublicKeyBytes = decodeBase64(driveKeys.publicKey);
70+
const prefix = new TextEncoder().encode('atomicdata.drive');
71+
const hashInput = new Uint8Array(
72+
prefix.length + drivePublicKeyBytes.length,
73+
);
74+
hashInput.set(prefix, 0);
75+
hashInput.set(drivePublicKeyBytes, prefix.length);
76+
const digest = await crypto.subtle.digest('SHA-256', hashInput);
77+
const driveHash = toHex(new Uint8Array(digest).slice(0, 16));
4178

4279
await createAndNavigate(
4380
server.classes.drive,
4481
{
4582
[core.properties.name]: name,
4683
[core.properties.write]: [agent.subject],
4784
[core.properties.read]: [agent.subject],
85+
[DRIVE_PUBLIC_KEY]: driveKeys.publicKey,
86+
[DRIVE_PRIVATE_KEY]: driveKeys.privateKey,
87+
[DRIVE_HASH]: driveHash,
4888
},
4989
{
5090
noParent: true,
5191
skipNavigation,
5292
onCreated: async resource => {
5393
// Add drive to the agents drive list.
54-
const agentResource = await store.getResource(agent.subject!);
55-
agentResource.push(server.properties.drives, [resource.subject]);
56-
await agentResource.save();
94+
// DID agents may not have a local resource, so we ignore errors here.
95+
try {
96+
const agentResource = await store.getResource(agent.subject!);
97+
agentResource.push(server.properties.drives, [resource.subject]);
98+
await agentResource.save();
99+
} catch (_e) {
100+
// Agent resource update failed (e.g., DID agents don't have a local resource)
101+
}
57102

58103
// Create a default ontology.
59104
const ontologyName = stringToSlug(name.trim());
60105
const ontology = await store.newResource({
61-
subject: `${resource.subject}/defaultOntology`,
62106
isA: core.classes.ontology,
63107
parent: resource.subject,
64108
propVals: {

browser/data-browser/src/helpers/AppSettings.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,13 @@ export const AppSettingsContextProvider = (
5454

5555
const setDrive = useCallback(
5656
(newDrive: string) => {
57-
const url = new URL(newDrive);
5857
innerSetDrive(newDrive);
59-
setBaseURL(url.origin);
60-
serverURLStorage.set(url.origin);
58+
59+
if (newDrive.startsWith('http://') || newDrive.startsWith('https://')) {
60+
const url = new URL(newDrive);
61+
setBaseURL(url.origin);
62+
serverURLStorage.set(url.origin);
63+
}
6164
},
6265
[innerSetDrive, setBaseURL],
6366
);

browser/data-browser/src/hooks/useCreateAndNavigate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function useCreateAndNavigate(): CreateAndNavigate {
5050
await resource.save();
5151

5252
if (onCreated) {
53-
onCreated(resource);
53+
await onCreated(resource);
5454
}
5555

5656
if (!skipNavigation) {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3438,3 +3438,11 @@ msgstr ""
34383438
#: src/views/InvitePage.tsx
34393439
msgid "Failed to persist agent after accepting invite"
34403440
msgstr ""
3441+
3442+
#: src/views/InvitePage.tsx
3443+
msgid "Invite accepted, but no destination was returned."
3444+
msgstr ""
3445+
3446+
#: src/components/forms/NewForm/SubjectField.tsx
3447+
msgid "The identifier of the resource. DID subjects are determined by the genesis commit signature."
3448+
msgstr ""

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3457,3 +3457,11 @@ msgstr "Failed to persist agent after accepting invite"
34573457
#: src/routes/Search/SearchRoute.tsx
34583458
msgid "Searching for <0/>..."
34593459
msgstr "Searching for <0/>..."
3460+
3461+
#: src/views/InvitePage.tsx
3462+
msgid "Invite accepted, but no destination was returned."
3463+
msgstr "Invite accepted, but no destination was returned."
3464+
3465+
#: src/components/forms/NewForm/SubjectField.tsx
3466+
msgid "The identifier of the resource. DID subjects are determined by the genesis commit signature."
3467+
msgstr "The identifier of the resource. DID subjects are determined by the genesis commit signature."

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,3 +3424,11 @@ msgstr ""
34243424
#: src/views/InvitePage.tsx
34253425
msgid "Failed to persist agent after accepting invite"
34263426
msgstr ""
3427+
3428+
#: src/views/InvitePage.tsx
3429+
msgid "Invite accepted, but no destination was returned."
3430+
msgstr ""
3431+
3432+
#: src/components/forms/NewForm/SubjectField.tsx
3433+
msgid "The identifier of the resource. DID subjects are determined by the genesis commit signature."
3434+
msgstr ""

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3437,3 +3437,11 @@ msgstr ""
34373437
#: src/views/InvitePage.tsx
34383438
msgid "Failed to persist agent after accepting invite"
34393439
msgstr ""
3440+
3441+
#: src/views/InvitePage.tsx
3442+
msgid "Invite accepted, but no destination was returned."
3443+
msgstr ""
3444+
3445+
#: src/components/forms/NewForm/SubjectField.tsx
3446+
msgid "The identifier of the resource. DID subjects are determined by the genesis commit signature."
3447+
msgstr ""

browser/data-browser/src/views/ChatRoomPage.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,7 @@ export function ChatRoomPage({ resource }: ResourcePageProps) {
5757
e?.preventDefault();
5858

5959
if (!disableSend) {
60-
const subject = store.createSubject(resource.subject);
61-
6260
const msgResource = await store.newResource({
63-
subject,
6461
parent: resource.subject,
6562
isA: dataBrowser.classes.message,
6663
propVals: {

0 commit comments

Comments
 (0)