Context
With the DID migration, resources no longer live under HTTP-hierarchical paths like http://server.com/drive/website. Instead, each resource gets its own independent did:ad:<signature> subject derived from its genesis commit signature. This breaks the current localId resolution logic used during template imports.
Current behavior
When importing JSON-AD with localId properties (e.g., a website template), the server's generate_id_from_local_id() in lib/src/parse.rs constructs subjects by appending localId to the parent/importer subject:
fn generate_id_from_local_id(importer_subject: &str, local_id: &str) -> String {
format!("{}/{}", importer_subject, local_id)
}
op
When the importer/parent is a DID-based drive (e.g., did:ad:abc123), this produces subjects like did:ad:abc123/website, which:
- Violates the DID-first principle (DIDs should NOT have subpaths)
- Creates subjects that can't be fetched via HTTP (the
create-template CLI expects http://server/website)
- Breaks cross-references between resources within the same template
Desired behavior
Each localId-based resource imported during template application should get its own independent DID, generated from a genesis commit signature. The localId value should be persisted as a property on the resource for lookup purposes, which I think is already the case.
Scope of changes
1. Server: Two-pass import system (lib/src/parse.rs)
The import pipeline needs a two-pass approach:
Pass 1 — Create resources and collect DID mappings:
- For each resource in the import JSON-AD, create a genesis commit
- Sign it to derive the DID (
did:ad:<genesis-signature>)
- Store the
localId as a property on the resource
- Build a
HashMap<String, String> mapping localId → DID
Pass 2 — Rewrite cross-references:
- Walk through all
AtomicUrl-typed property values
- Replace any
localId references (e.g., "website/class/page", "01j5zrevq917dp0wm4p2vnd7nr") with their resolved DID from the mapping
- Save the final resources with correct cross-references
Key function changes:
generate_id_from_local_id() — remove or repurpose; no longer appends localId to parent
try_to_subject() — needs to check the localId → DID mapping before falling back to URL parsing
parse_json_ad_resource() — needs access to the mapping for reference resolution
pull_parents_of_props_to_front() — may need adjustment for the two-pass approach
2. Server: Import endpoint response
The /import endpoint (or the ParseOpts pipeline) should return the localId → DID mapping in its response so the frontend knows the actual subjects of imported resources.
Currently, importJsonAD in store.ts just posts to the import endpoint and expects success. It would need to receive and use the mapping to:
- Navigate to the correct root resource after import
- Add the correct DIDs to the drive's
subResources list
3. Frontend: ApplyTemplateDialog.tsx
Currently constructs expected subjects via URL concatenation:
const subjects = template?.rootResourceLocalIDs.map(localID => {
const base = drive.startsWith('did:') ? store.getServerUrl() : drive;
return new URL(localID, base + '/').toString();
}) ?? stableArray;
This needs to:
- After import, receive the
localId → DID mapping from the server response
- Use the mapping to find the DID for each
rootResourceLocalID
- Push the resolved DIDs (not constructed URLs) to the drive's
subResources
- Navigate to the resolved DID of the first root resource
4. Frontend: store.importJsonAD()
Needs to return the localId → DID mapping from the server response:
public async importJsonAD(
jsonADString: string,
options: ImportJsonADOptions,
): Promise<Record<string, string>> { // localId → DID mapping
// ...
}
5. create-template CLI (browser/create-template/)
The CLI currently hardcodes expected resource paths:
// templates.ts
ontologyID: ({ serverUrl }) => `${serverUrl}/website`,
generateEnv: ({ serverUrl }) => {
const siteSubject = `${serverUrl}/01j5zrevq917dp0wm4p2vnd7nr`;
// ...
}
This needs to change to query-based lookup:
- Query the server for resources with a matching
localId property under the given drive
- Use the returned DID as the ontology subject / site resource subject
- Alternatively, accept the ontology DID as a CLI argument (simpler)
6. Re-import / idempotency
When re-applying a template that was already imported:
- The system should query for existing resources with matching
localId values within the same parent/drive namespace
- If found, update the existing resources rather than creating duplicates
- The
localId must be unique within its parent namespace (enforced by query)
7. Website template data (browser/data-browser/src/components/Template/templates/website.tsx)
The template definition uses localId strings as cross-references between resources:
{
[core.properties.localId]: 'site-data',
[dataBrowser.properties.subResources]: [
'01j5zrd23mxam4mdg2ak97gqcm', // ← these are localId refs
'01j5zrevq917dp0wm4p2vnd7nr',
// ...
],
}
These cross-references need to be resolved during the two-pass import. The template data itself doesn't need to change — the import system handles the rewriting.
8. Tests
browser/e2e/tests/template.spec.ts — currently skipped with test.describe.skip(). Re-enable after implementation.
lib/src/parse.rs tests — update existing import_resource_with_localid, import_resource_with_json, references_to_localid_resources tests to verify DID generation instead of path concatenation.
Implementation order (suggested)
- Server
parse.rs — implement two-pass import with localId → DID mapping
- Server import endpoint — return the mapping in the response
- Update
parse.rs tests
- Frontend
store.importJsonAD() — parse and return the mapping
- Frontend
ApplyTemplateDialog — use mapping for navigation and subResources
create-template CLI — switch to query-based or DID-argument lookup
- Re-enable and update
template.spec.ts e2e tests
- Handle re-import idempotency
Related
Context
With the DID migration, resources no longer live under HTTP-hierarchical paths like
http://server.com/drive/website. Instead, each resource gets its own independentdid:ad:<signature>subject derived from its genesis commit signature. This breaks the currentlocalIdresolution logic used during template imports.Current behavior
When importing JSON-AD with
localIdproperties (e.g., a website template), the server'sgenerate_id_from_local_id()inlib/src/parse.rsconstructs subjects by appendinglocalIdto the parent/importer subject:op
When the importer/parent is a DID-based drive (e.g.,
did:ad:abc123), this produces subjects likedid:ad:abc123/website, which:create-templateCLI expectshttp://server/website)Desired behavior
Each
localId-based resource imported during template application should get its own independent DID, generated from a genesis commit signature. ThelocalIdvalue should be persisted as a property on the resource for lookup purposes, which I think is already the case.Scope of changes
1. Server: Two-pass import system (
lib/src/parse.rs)The import pipeline needs a two-pass approach:
Pass 1 — Create resources and collect DID mappings:
did:ad:<genesis-signature>)localIdas a property on the resourceHashMap<String, String>mappinglocalId → DIDPass 2 — Rewrite cross-references:
AtomicUrl-typed property valueslocalIdreferences (e.g.,"website/class/page","01j5zrevq917dp0wm4p2vnd7nr") with their resolved DID from the mappingKey function changes:
generate_id_from_local_id()— remove or repurpose; no longer appendslocalIdto parenttry_to_subject()— needs to check thelocalId → DIDmapping before falling back to URL parsingparse_json_ad_resource()— needs access to the mapping for reference resolutionpull_parents_of_props_to_front()— may need adjustment for the two-pass approach2. Server: Import endpoint response
The
/importendpoint (or theParseOptspipeline) should return thelocalId → DIDmapping in its response so the frontend knows the actual subjects of imported resources.Currently,
importJsonADinstore.tsjust posts to the import endpoint and expects success. It would need to receive and use the mapping to:subResourceslist3. Frontend:
ApplyTemplateDialog.tsxCurrently constructs expected subjects via URL concatenation:
This needs to:
localId → DIDmapping from the server responserootResourceLocalIDsubResources4. Frontend:
store.importJsonAD()Needs to return the
localId → DIDmapping from the server response:5.
create-templateCLI (browser/create-template/)The CLI currently hardcodes expected resource paths:
This needs to change to query-based lookup:
localIdproperty under the given drive6. Re-import / idempotency
When re-applying a template that was already imported:
localIdvalues within the same parent/drive namespacelocalIdmust be unique within its parent namespace (enforced by query)7. Website template data (
browser/data-browser/src/components/Template/templates/website.tsx)The template definition uses
localIdstrings as cross-references between resources:These cross-references need to be resolved during the two-pass import. The template data itself doesn't need to change — the import system handles the rewriting.
8. Tests
browser/e2e/tests/template.spec.ts— currently skipped withtest.describe.skip(). Re-enable after implementation.lib/src/parse.rstests — update existingimport_resource_with_localid,import_resource_with_json,references_to_localid_resourcestests to verify DID generation instead of path concatenation.Implementation order (suggested)
parse.rs— implement two-pass import withlocalId → DIDmappingparse.rstestsstore.importJsonAD()— parse and return the mappingApplyTemplateDialog— use mapping for navigation and subResourcescreate-templateCLI — switch to query-based or DID-argument lookuptemplate.spec.tse2e testsRelated
store.newResource()local-first genesis commit signing