Status: Stable v1.1 — 2026-09-30 (v1.0 — 2026-05-18)
Compatibility: Linko exports
schema_version: "1.1"and imports"1.0"and"1.1". 1.1 only extends the Resourcetypevocabulary (post,tool;online_courseretired). Future minor versions may add optional fields or values only after importer support is explicitly implemented; unsupported versions must fail clearly rather than being read silently.
Purpose
This document is the authoritative, machine-friendly specification of Linko's exported vault format. Any tool — Linko's own importer, third-party scripts, AI agents, or future clients — that conforms to this spec can read and write Linko vault data with functional equivalence within v1's defined scope.
The spec is the contract behind Linko's promise:
"Linko handles intelligent organization; users always own their original content."
1. Versioning
- The schema follows semantic-ish versioning:
MAJOR.MINOR. - Major bumps (e.g., v1 → v2) introduce new top-level directories or new required fields. Old importers MUST NOT read major-newer packages silently — they must error.
- Minor bumps (e.g., v1.0 → v1.1) may add optional fields or vocabulary values; Linko's importer accepts only the minor versions it implements (currently
"1.0"and"1.1"). - Every export package declares its version in
linko-schema.json.
2. Vault Layout
A v1 export package is a directory (typically delivered as a ZIP) with the following structure:
my-linko-vault/
├── linko-schema.json # REQUIRED — package manifest
├── export-warnings.json # RECOMMENDED — exporter warning report
├── notes/ # REQUIRED — one file per note
│ ├── <yyyy-mm-dd>-<slug>.md
│ └── ...
├── resources/ # REQUIRED — one file per resource
│ ├── <slug>.md
│ └── ...
└── README.md # RECOMMENDED — human-readable export notes
Filename conventions:
- Notes:
<creation-date>-<slug>.mdwhere slug is a URL-safe lowercase variant of the note title (max 80 chars). If two notes collide on date+slug, append-2,-3, etc. - Resources:
<slug>.mdwhere slug is derived fromtitle(max 80 chars), with collision suffixes as above. - All filenames MUST be UTF-8 and ASCII-safe (replace forbidden chars
<>:"/\|?*with_). - Filenames are for humans only. Importers MUST use frontmatter UUIDs, not filenames, as object identity.
- UUIDs SHOULD NOT be included in filenames in v1 exports.
Empty directories MUST still exist (the importer uses their presence to assert v1 compliance).
Exporters SHOULD generate README.md by default. It should explain the schema version, basic import instructions, strict import behavior, and the fact that private notes are exported as plaintext for the account owner. Importers MUST NOT require README.md.
Exporters SHOULD generate export-warnings.json by default. Importers MUST NOT require it when the manifest does not declare a warning report, but tools MUST surface it to users when the manifest declares it and the file is present.
3. linko-schema.json — Package Manifest
This file is the entry point for any importer. It MUST exist at the root of every package.
Schema
{
"schema_version": "1.1",
"generated_at": "2026-04-22T10:00:00Z",
"generator": "linko-exporter/1.0",
"export_id": "4d967c2a-3d6a-42a7-96b5-1ad2a8fbf1a1",
"linko_origin_signature": "opaque-server-generated-signature",
"contents": {
"notes": {
"count": 342,
"directory": "notes/"
},
"resources": {
"count": 58,
"directory": "resources/"
},
"projects": {
"count": 12,
"source": "manifest"
},
"tags": {
"count": 127,
"source": "frontmatter"
},
"warnings": {
"count": 0,
"file": "export-warnings.json"
}
},
"projects": [
{
"id": "222e4567-e89b-12d3-a456-426614174001",
"name": "Product",
"scope": "custom",
"parent_id": null,
"description": "User-owned product work"
},
{
"id": "222e4567-e89b-12d3-a456-426614174002",
"name": "Linko",
"scope": "custom",
"parent_id": "222e4567-e89b-12d3-a456-426614174001"
}
]
}
Field reference
| Field | Type | Required | Description |
|---|---|---|---|
schema_version |
string | Yes | "1.1" for this spec ("1.0" packages remain importable) |
generated_at |
ISO 8601 datetime (UTC) | Yes | Export timestamp |
generator |
string | Yes | <tool>/<version>; e.g. linko-exporter/1.0 |
export_id |
UUID string | Yes | Identifier for this export package |
linko_origin_signature |
string | No | Opaque server-generated proof used by Linko to recognize a full export created for the importing account. It contains no account identity and is not required for third-party v1 packages. |
contents.notes.count |
integer | Yes | Sanity check — must equal actual file count |
contents.notes.directory |
string | Yes | Always "notes/" in v1 |
contents.resources.count |
integer | Yes | Sanity check |
contents.resources.directory |
string | Yes | Always "resources/" in v1 |
contents.projects.count |
integer | No | Number of the user's Tags declared in manifest projects |
contents.projects.source |
string | No | Always "manifest" when Tag hierarchy metadata is present |
contents.tags.count |
integer | Yes | Number of unique tag IDs referenced in note/resource frontmatter |
contents.tags.source |
string | Yes | Always "frontmatter" in v1 |
contents.warnings.count |
integer | No | Number of exporter warnings |
contents.warnings.file |
string | No | Warning report path, usually "export-warnings.json" |
The manifest MUST NOT include user UUID, email, username, auth identifiers, or other PII. Import targets are determined by the logged-in account that invokes the importer.
Conformance vs current Linko enforcement: Linko validates the required manifest metadata, fixed directory declarations, Note, Resource, Tag (projects) and tag reference counts, and any declared warning report before writing. Packages that omit the optional projects or warnings sections remain valid v1 packages for backward compatibility. Unsupported schema versions still fail clearly.
Manifest projects
projects is an optional additive v1 field for the user's Tag hierarchy. The manifest key is projects for compatibility; it holds the user's Tags (called Projects in earlier versions of Linko). Linko exports every Tag the user owns, including Tags that are not currently attached to a Note or Resource. Adapters should use the same normalized structure when a source exposes folders, collections, notebooks, or other user-authored organization that must preserve parent-child structure. Packages without projects remain valid v1 packages and import with the older flat tag behavior.
Each entry has this shape:
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID string | Yes | Stable Tag identifier. Importers reuse the target user's Tag with this UUID or create one. |
name |
string | Yes | Tag display name. Same names are allowed under different parents. |
scope |
enum | Yes | Must be "custom" in v1. Topics are not valid entries. |
parent_id |
UUID string or null | Yes | Parent Tag id from the same manifest projects list, or null for a top-level Tag. |
description |
string | No | The Tag's description, when the user wrote one. |
icon_key |
string or null | No | Portable user-selected Tag icon key. It must match Linko's lowercase ASCII icon-key pattern. |
hidden_in_workspace_nav |
boolean | No | User's workspace navigation visibility preference; defaults to false when omitted. |
Importers MUST validate the Tag hierarchy before writing:
projectsentries must have unique UUIDs.- Every
parent_idmust reference another Tag in the sameprojectslist. - Duplicate Tag names under the same parent are blocking package errors; the same name under different parents is valid.
- References to Topics, self edges, cycles, and parent edges to a Topic or to another user's Tag are blocking package errors.
- Created or updated Tags must be owned by the importing user, never Topics.
- An importer that keeps derived data for Tags (such as search embeddings) should refresh it for the Tags it creates or updates.
Note and Resource tags remain the authority for which Tags an object belongs to. The manifest-level projects list defines Tag metadata and hierarchy; it does not by itself attach a Note or Resource to a Tag.
4. Note Files
Each note is one Markdown file with YAML frontmatter.
Format
---
id: 550e8400-e29b-41d4-a716-446655440000
title: "Decision points for AI agent architecture"
created: 2026-03-15T10:32:00Z
updated: 2026-03-15T10:32:00Z
private: false
is_voice_note: false
source:
type: manual
detail: web_app
category: personal_thought
tags:
- id: 111e4567-e89b-12d3-a456-426614174000
name: "ai/agents"
scope: public
- id: 222e4567-e89b-12d3-a456-426614174001
name: "product/linko"
scope: custom
resources:
- id: 333e4567-e89b-12d3-a456-426614174002
title: "GPT-5 Technical Report"
---
# Decision points for AI agent architecture
Notes after reading the GPT-5 technical report...
Frontmatter field reference
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID string | Yes | Stable note identifier; Linko keeps it as the note's own ID |
title |
string | Yes | Note title; may be empty string but field MUST exist |
created |
ISO 8601 datetime | Yes | Original creation time, UTC |
updated |
ISO 8601 datetime | Yes | Last modification time, UTC |
private |
boolean | Yes | Product-level privacy flag |
is_voice_note |
boolean | No | Default false |
source |
object | No | How this note entered Linko; see below |
category |
string | No | What kind of note this is; see below |
voice_transcript |
string | No | Original voice transcript for voice notes, when available |
tags |
array of {id, name, scope} objects |
Yes | May be empty array; MUST NOT be omitted |
resources |
array of {id, title} objects |
Yes | May be empty array; MUST NOT be omitted. title is a human-readable snapshot; id is import authority |
links |
array of {id, kind, context?} objects |
No | Note-to-note references; id is the target Note UUID |
source and category
source describes how the note entered Linko. Suggested source.type values include manual, imported, ai_created, extension_capture, voice_transcription, and api. source.detail may hold a more specific entry point such as web_app, mobile_app, chrome_extension, obsidian, readwise, weread, linko_voice, or wechat_import.
category describes the primary content type of the note. Linko exports its note type here: insight, excerpt, idea, todo, or log, and omits the field when a note has no type. Linko's importer restores those values as the note type chosen by the user, and treats the legacy values highlight and quote as excerpt. Other legacy values that earlier exports or adapters produced (personal_thought, conversation, ai_conversation, voice_note, imported_text, summary, other) remain valid input but carry no content type; capture method belongs in source.
These values are intentionally extensible controlled vocabularies. The suggested values above are valid for v1, but Linko may add or adjust values as product needs evolve. Exporters SHOULD populate source and category when known. Importers MUST preserve source when a Linko origin signature verifies for the target account, MUST map category to the note type as described above, and MUST tolerate unknown string values for forward compatibility. Cross-account Linko packages, external packages, and adapter-generated imports MUST persist source.type = imported on the target Note; source.detail should identify the import route. This keeps target-side capture filters aligned with how the Note entered the target account while retaining exact source fields for same-account restore.
Body
The Markdown body following the closing --- is the note's content as written by the user. Linko will not transform it (no auto-linking, no canonicalization beyond ensuring valid Markdown). On import, the Note title comes from frontmatter title; the body after frontmatter is the Note content.
Private notes: private: true means the note is private in Linko. In Linko, private notes MUST be stored encrypted server-side. V1 exports are designed for the account owner and remain human-readable, so Linko writes decrypted plaintext title and body into the Markdown file. Export UI MUST warn that the package may contain private plaintext content. Package-level encryption is out of scope for v1.
Voice notes: The Markdown body is the note body shown in Linko, which may be the organized note generated from the voice input. When available, the original transcript SHOULD be exported separately as frontmatter voice_transcript. V1 does not include original audio binaries. The is_voice_note: true flag preserves the type.
Body Markdown
The dialect of a note body: what Linko's editors read and write, inside Linko and in exported Note files.
Unchanged body. When the user has not changed a note's body, every surface sends the stored Markdown byte for byte, never a re-serialised copy. A re-serialised body is saved only after a real edit. The API's own write step is outside this rule: it strips raw HTML tags from every body it saves (an injection guard), so a body that holds raw HTML (such as <u>) loses its tags on any save, edited or not; every other byte is stored as sent. "Not changed" means the editor's document is still the one it opened: typing and undoing, IME composition, plugin transactions and title-only edits do not count, while removing a mark the writer cannot express (such as strikethrough on web) does. The web note editor, the note page, the ChatGPT app editor, the extension's restored drafts, the native mobile editor and its fallback editor all follow it, and their save-time normalization (trimming, blank-line runs) applies only to an edited body.
Base. CommonMark plus GFM tables, task lists, strikethrough and autolinks, with three amendments and three Linko conventions:
- A single newline is a line break (
breaks: true), not a space. - CJK-friendly emphasis:
**and*open and close beside CJK punctuation (**结论:**先写), asmarkdown-it-cjk-friendly,marked-cjk-friendlyandremark-cjk-friendlyread it. - Bare addresses link only with a scheme (
https://…) and run to the next space, as GFM autolinks do, so CJK text right after an address is part of it. Writers write an address that text follows as an autolink (<https://…>,) or a link. - Empty paragraph:
U+200Balone on a line is an intentionally empty paragraph. A run of them saves as one. - Private images:
references a private image. Clients keep the reference; the API may send a signed URL instead. - Headings: a note has one heading level, written
##;#is reserved for the note title. Readers open#to######as that one heading, and the next edit writes it##. - No underline. Markdown has none and no editor offers it. Readers open
<u>…</u>as plain text (the words stay); no writer emits<u>. Clients send a stored<u>back unchanged until the note is next edited (Unchanged body), and cards may still show it until then.
Canonical forms. What a writer saves for each construct after an edit:
| Construct | Saved as | Also read |
|---|---|---|
| Paragraph | Text, one blank line between paragraphs | |
| Line break | A single newline | Two trailing spaces, \ before the newline, <br> |
| Bold | **text** |
__text__ |
| Italic | *text* |
_text_ |
| Strikethrough | ~~text~~ |
|
| Inline code | `code`, with a longer backtick fence when the code holds backticks |
|
| Code block | A fenced block (``` plus an optional language) |
Indented code |
| Heading | ## text |
# to ###### |
| Link | [text](address "title"); a link whose text is its address as [address](address) |
Autolinks, bare addresses |
| Image |  |
|
| Bullet list | - item, nested items indented to the item's text; the second of two adjacent lists uses * |
*, + |
| Ordered list | 1. item, keeping its start number; the second of two adjacent lists uses ) |
|
| Checklist | - [ ] item and - [x] item, also in ordered lists |
[X] |
| Quote | > on each line; > alone between its paragraphs |
|
| Table | A GFM pipe table: header row, ` | --- |
| Thematic break | --- |
***, * * * |
| Underline | Not written: the words only | <u>…</u> |
| Raw HTML | Its text | Editors open it as its text; cards render only an allow-list of tags; the API strips tags on save |
| Literal text | As typed, escaping only what a reader would take as syntax |
Older content follows the same rules: bodies from the Draft.js editor (2024–2025) that were never converted are JSON text, which every editor opens and saves as text; Markdown written by the Draft.js converter (html2text) reads as ordinary Markdown.
Export Warnings
Open Format exporters SHOULD complete a package whenever possible. Object-level issues SHOULD be recorded in export-warnings.json instead of failing the whole export:
- If an optional field cannot be exported safely, skip that field and add a warning.
- If a single Note or Resource cannot be exported safely because required data is unreadable, skip that object and add a warning.
- If private Note title/body decryption fails, exporters MUST NOT write placeholder text such as
"Encrypted"into the Markdown file. They SHOULD skip that Note and add a warning. - Whole-package failure SHOULD be reserved for system-level errors such as database connection failure, ZIP write failure, or manifest/schema generation failure.
Warning report shape:
{
"generated_at": "2026-05-07T04:30:00Z",
"warnings": [
{
"code": "field_skipped",
"object_type": "note",
"object_id": "111e4567-e89b-12d3-a456-426614174000",
"field": "voice_transcript",
"message": "Voice transcript could not be decrypted; note body was exported."
}
]
}
Cross-references
tags[].idis a stable tag UUID; tag objects are embedded directly in frontmatter in v1resources[].idMUST match a resource UUID inresources/(i.e., a file's frontmatterid).resources[].titleis informational only and MUST NOT be used as identity.links[].idMUST be a valid Note UUID string and SHOULD match another Note UUID in the same package. Missing link referents SHOULD be skipped with a warning because note links are optional auxiliary structure.- Strict import MUST validate required tag/resource references; missing required resource referents are errors.
Supported links[].kind values are reference, builds_on, and contrasts. Importers SHOULD skip unknown link kinds with a warning rather than failing the package. links[].context is optional short text explaining the relationship; non-string context should be dropped with a warning.
5. Resource Files
Each resource is one Markdown file with YAML frontmatter.
Format
---
id: 333e4567-e89b-12d3-a456-426614174002
resource_id: 444e4567-e89b-12d3-a456-426614174003
title: "GPT-5 Technical Report"
type: article
author: "OpenAI"
year: 2025
month: 8
platform: "arxiv"
url: "https://arxiv.org/abs/..."
image_url: "https://..."
language: en
description: "Technical details of GPT-5 architecture..."
created: 2026-03-15T10:32:00Z
updated: 2026-03-15T10:32:00Z
progress: 75
total_length: 100
custom_title: "GPT-5 paper — my highlights"
tags:
- id: 555e4567-e89b-12d3-a456-426614174004
name: "highlights"
scope: custom
---
# GPT-5 Technical Report
OpenAI · Article · 2025
[Open resource](https://arxiv.org/abs/...)
Frontmatter field reference
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID string | Yes | Stable user-resource identifier: the user's own link to this resource, which holds their progress, custom title and tags |
resource_id |
UUID string | Yes | Stable global resource identifier, the same for every account that saved this resource |
title |
string | Yes | Resource title |
type |
enum | Yes | One of: book, article, post, video, podcast, tool, other (1.1). online_course is a 1.0 value that importers still accept and read as video; exporters no longer write it. |
author |
string | No | |
year |
integer | No | Publication year |
month |
integer | No | Publication month (1–12) |
platform |
string | No | e.g., arxiv, youtube; only for non-book types |
url |
URL | No | External link; omitted for books when Linko only has an internal provider lookup URL such as Google Books API or Douban |
image_url |
URL | No | External cover image reference. It is not a guarantee that the image remains available. Linko private cover storage keys, signed URLs, and cover binaries are never exported in v1. |
language |
string | No | ISO 639-1 code (en, zh, etc.) |
description |
string | No | Resource description |
created |
ISO 8601 datetime | Yes | When user added this resource |
updated |
ISO 8601 datetime | Yes | |
progress |
integer | No | 0–100 (percentage); default 100 |
total_length |
integer | No | Default 100 |
custom_title |
string | No | User's renamed title for this resource |
tags |
array of {id, name, scope} |
Yes | User-visible tags applied to this resource; may be empty |
V1 intentionally excludes system-only resource data: Linko's own topic suggestions, author details, the resource-level privacy flag, and the record of how the resource was first added. They support internal recommendation and organization, future product features, or backend-only metadata, and are not portable Resource content. Source adapters may use a documented frontmatter source object when they need to preserve source provenance.
Body
The body provides a lightweight human-readable summary. It SHOULD NOT repeat long description content or structural fields such as tags, progress, or custom title. For resources, frontmatter is the import authority; importers MAY ignore the body and rebuild it from frontmatter.
For books, Linko may omit url when its stored link is only an internal provider lookup URL (for example Google Books API or Douban). ISBN is not part of v1 because Linko does not store it today; it may be added later as an optional field.
6. Tag References
V1 does not include a standalone tags.json. Tags are embedded in Note and Resource frontmatter as references.
Tag object
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID string | Yes | Stable tag identifier |
name |
string | Yes | Display name at export time |
scope |
enum | Yes | public for Linko-maintained public tags; custom for user-created tags |
V1 tag semantics
- The same tag may appear in many files; importers should deduplicate by
id. - Manifest
contents.tags.countcounts unique tag IDs referenced across all exported files. publictags (Topics) MUST already exist in the target Linko. If a public tag UUID is missing, strict import records a warning and skips that tag reference.- For
publictag references whose UUID exists in the target, the target's currentnameis authoritative; the embeddednameis informational only (it preserves a snapshot for human reading and cross-tool compatibility, but importers MUST NOT use it to rename existing tags). customtags are user-owned. If a custom tag UUID is missing in the target account, strict import creates it from the embedded{id, name, scope}object.- For
customtag references whose UUID already exists in the target account, the embeddednameis ignored (no rename via import). Tag rename happens only through Linko's UI. - If the target account already contains a
customtag with the samenamebut a different UUID, strict import MUST still create the new custom tag with its embedded UUID (so all references are preserved), and MUST emit a warning identifying the duplicate. v1 does not auto-merge tags by name; reconciling duplicate display names is left to the user. - In Linko,
publictags appear as Topics, which Linko maintains, andcustomtags as the user's own Tags. These are product names over the v1scopefield; they do not change v1 file semantics. - V1 preserves the parent-child relationships between the user's Tags when they are declared in manifest
projects. - V1 does not preserve Topic descriptions or any tag translations (Tag descriptions travel in manifest
projects). - V1 does not export derived hierarchy data (such as a Tag's full list of ancestors); importers MUST rebuild it rather than expect it.
- A future additive version may introduce
tag_edges.json, richer tag metadata, aprojects/directory, or Topic hierarchy semantics.
7. Round-trip Semantics
The defining promise of this format:
Export a vault → clear the database → import the same vault → the database state is functionally equivalent to before the export, restricted to v1's data scope.
"Functionally equivalent" means:
Preserved:
- All Notes (id, title, body, dates, tags, resources, private, voice flag, and source/category when present; cross-account imports normalize the target source type to
imported) - All Resources in an empty-target round trip (global resource identity and metadata + user-resource identity and relationship fields)
- All user-visible tag references (
id,scope, and the embeddednamesnapshot); display names on import follow §6 tag authority rules - The user's Tag hierarchy declared in manifest
projects - Tag descriptions, icon keys, and workspace visibility preferences from manifest
projects, when present - All cross-references between the above
Not preserved (intentional, documented):
- Internal: AI suggestion history
- Derived: embeddings and other derived search and hierarchy data
- The Topic tree in v1
- Topic descriptions and tag translations in v1
- Note edit counters
- Social: likes, follows, push tokens
- Billing / membership state
- Auth tokens
- Encrypted-form fields (private notes are re-encrypted on import)
- Voice audio binaries
- Resource original files / images, including private cover storage objects
A targeted round-trip test exists for the preserved set and should be run for Open Format changes. The full Open Format exporter/importer and round-trip suites are part of the focused verification for contract changes.
8. Conformance
A tool is v1-conformant if it:
- Reads
linko-schema.jsonfirst and refuses unsupported versions clearly - Validates
contents.*.countagainst actual file counts - Resolves note/resource cross-references during import
- Applies V1 tag import rules for
publicandcustomtag references - Does not rely on unknown optional frontmatter fields for v1.0 behavior; future minor-version support should warn or preserve explicitly rather than silently dropping meaningful data
- Does not infer, repair, or fabricate missing required values in strict mode
- Round-trips its own exports without data loss
Verified Linko-generated full-account exports may exceed the general 500-item reliability guard used for external source imports. Their import preflight reports a warning instead of blocking the complete package. A manifest generator value alone is not sufficient to receive this exception. External Open Format packages remain subject to the general guard and should be split by the source tool when necessary.
Importers MAY also apply transport-safety limits for raw ZIP bytes, expanded ZIP bytes, and member count. Linko currently limits Open Format imports to 100 MiB compressed input, 500 MiB expanded content, and 20,000 ZIP members; these limits are separate from the Note/Resource item-count policy.
Import modes
V1 defines only strict import:
- Validate the manifest, schema version, directory layout, required files, required fields, UUID formats, counts, and cross-references.
- If validation fails, abort the entire import before writing data.
- Return a structured error report that identifies every blocking issue found.
- Support
dry_runso users can validate a package without writing to the database. - Importing the same package multiple times MUST be idempotent by UUID.
- Drift protection (stale-package safety): import authority is object-specific:
- For
Note.id, when the UUID matches an existing Note in the target account, the importer MUST compare Noteupdatedtimestamps. If the target'supdatedis newer than the package's, skip that Note and emit a warning ("target object newer than package — kept target version"). If timestamps are equal, treat the Note as unchanged and do not physically rewrite it. Only if the package'supdatedis newer than the target's should the importer overwrite the target Note with package data. - For Resource file
id(the user-resource), drift comparison applies only to user-owned relationship fields:created,updated,progress,total_length,custom_title, andtags. If the target user-resource is newer, skip those user-owned fields and emit a warning. If timestamps are equal, treat the user-resource as unchanged. Only if the package'supdatedis newer should the importer overwrite user-owned relationship fields from the package. - For global
resource_id, V1 import is conservative because global Resource facts may be shared across accounts. If the target lacksresource_id, create the global Resource from package metadata. If the target already hasresource_id, attach the user-resource to the existing global Resource and MUST NOT overwrite global Resource metadata from a user import. If package global metadata differs from target metadata, the importer MAY emit a warning. - Tag references follow §6 — tag names are not subject to drift comparison because they are never overwritten via import.
- Importers MUST NOT silently overwrite newer user data with stale package data.
- For
- Additive only — never delete: objects present in the target account but absent from the package MUST NOT be deleted. V1 import is upsert-only. Full restore from a package requires importing into an empty target account; v1 does not provide a "mirror" or "destructive sync" mode.
- User-owned object IDs (
Note.id, Resource fileid, and custom tag IDs) are idempotent within the target account. Cross-account imports may remap user-owned IDs if they conflict in the target account, but MUST preserve all references through an import-local mapping. - Global resource IDs (
resource_id) identify globalResourcefacts and may be reused across accounts. If the target database does not containresource_id, V1 import creates a global Resource with that UUID from the exported frontmatter. V1 import MUST NOT perform heuristic resource deduplication by title, author, or URL; that can be added later as an explicit repair/reconciliation feature.
repair import is intentionally out of scope for v1. A repair importer may later recover user-edited or partially broken packages by filling missing fields, generating UUIDs, skipping bad references, or normalizing dates, but that behavior is not part of the Open Format v1 contract.
9. Reserved for Future Versions
These keys / directories MUST NOT be used by v1 implementations and are reserved:
| Reserved | Introduced in | Purpose |
|---|---|---|
subjects/ directory |
v2 | Topic pages and their revisions |
attachments/ directory |
v3 | Binary content addressing |
Frontmatter subjects: |
v2 | Note → Topic page references |
Frontmatter attachments: |
v3 | Note → Attachment references |
linko-schema.json attachment_index |
v3 | sha256 → file mapping |
tag_edges.json or tag parents |
v2+ | Tag tree relationships |
10. Examples
Linko's exporter, importer and round-trip tests are the current executable examples; an export from your own Linko account is a complete example package. A static reference vault can be added later when it becomes useful for third-party tooling.
11. Change Log
| Date | Version | Notes |
|---|---|---|
| 2026-04-22 | 1.0 Draft | Initial spec |
| 2026-05-01 | 1.0 Draft revision | Removed manifest email, clarified human-readable filenames, added global Resource and user-resource UUIDs, made tags flat in v1, documented private plaintext export and strict import |
| 2026-05-01 | 1.0 Draft revision | Clarified compatibility semantics, optional README, and source/category round-trip preservation |
| 2026-05-02 | 1.0 Draft revision | Clarified user-owned UUID remapping and public/custom tag import behavior |
| 2026-05-02 | 1.0 Draft revision | Clarified README generation, source/category preservation, Note/Resource import authority, and Resource creation without heuristic deduplication |
| 2026-05-02 | 1.0 Draft revision | Marked source/category as extensible vocabularies and defined README default content |
| 2026-05-06 | 1.0 Draft revision | Closed four hardening gaps: (1) v1 import declared additive only — absent objects are never deleted; (2) drift protection — importer skips with warning when target updated is newer than package, preventing stale-package overwrites; (3) tag-name authority rules — public tag uses the target's name; custom tag name set on creation only, ignored thereafter; (4) cross-account custom-tag duplicate-name conflict — create new tag with embedded UUID, warn, no auto-merge |
| 2026-05-06 | 1.0 Draft revision | Follow-up review fixes: removed the note edit counter from v1 export scope; split drift/import authority between Note, user-resource, and global Resource metadata; aligned tag-name round-trip wording with tag authority rules; converted examples and promise text to English |
| 2026-05-07 | 1.0 Draft revision | Tightened import drift semantics: equal timestamps are unchanged and should not physically rewrite existing objects; only package-newer objects overwrite target data |
| 2026-09-30 | 1.1 | Resource type adds post and tool; online_course retired (imported as video, no longer exported). Linko exports 1.1 and imports 1.0 and 1.1 |
| 2026-10-25 | 1.1 | Added Body Markdown: the body dialect, canonical forms, one ## heading level, no underline, and the unchanged-body Principle. Format version unchanged: existing bodies stay valid |