AGENTS.md — KSN-01
Purpose
KSN-01 is a proof of concept for a persistent personal layer on top of the web.
The current brick:
Add persistent personal links to text on any webpage without changing the page itself.
A user's annotations live outside the source page. A compatible client renders that layer at read time.
Principles
- Files are the durable artifact; software is a replaceable interpreter.
- No application. No database. No telemetry subsystem. Just HTTP doing what HTTP already does.
- Prefer static files and standard protocols until complexity earns its way in.
- Keep the source page untouched; render the user's layer at read time.
- Deterministic input should produce deterministic lookup and rendering.
- Make URL normalization a replaceable function, not infrastructure.
- Hash canonical identity, not incidental request state.
- Preserve raw inputs for observability without making them part of identity.
- Build the smallest brick that proves the behavior before designing the system around it.
- Prefer boring, inspectable, FOSS-compatible technology that can survive its original authors.
- Do not introduce frameworks, services, dependencies, or infrastructure merely because they are conventional.
Architecture
The system is intentionally static-file based:
web page
+
KSN-01 annotation JSON
+
client-side renderer
=
personalized view
Annotation lookup:
raw URL
↓
canonical URL
↓ cleanUrl()
normalized canonical URL
↓ SHA-256
<hash>.json
↓
static HTTP GET
The source page is never modified.
Annotation format
Use W3C Web Annotation concepts where practical, particularly TextQuoteSelector.
Example:
[
{
"selector": {
"type": "TextQuoteSelector",
"exact": "impossible dream",
"prefix": "looked like an ",
"suffix": ". The only car"
},
"href": "https://youtu.be/6FmXjxdDBRI"
}
]
The resolver currently uses exact, prefix, and suffix to locate text and maps the match back to a native DOM Range.
Do not invent a proprietary selector format without a concrete need.
URL identity
Keep these concepts distinct:
rawUrl: the requested page URL, retained for observability.canonicalUrl: normalized page identity used for annotation lookup.hash: SHA-256 ofcanonicalUrl.
API requests include both URLs as query parameters so ordinary webserver logs provide diagnostic information:
api/<hash>.json?rawUrl=...&canonicalUrl=...
Do not make rawUrl part of identity.
cleanUrl() is deliberately small and replaceable. It currently removes fragments and common tracking parameters while preserving potentially meaningful query parameters.
Do not move URL normalization server-side unless the client-side model demonstrably stops being sufficient.
API
The "API" is currently a directory of static JSON files.
Example:
api/
└── <sha256-of-canonical-url>.json
A missing annotation file is a normal condition:
404 + empty body
Do not turn absence into an application-level error.
Source and builds
src/pw.js is the single implementation source.
Its default base URL is:
const PW_BASE_URL = "http://localhost:8000/";
This allows the project to work with:
cd KSN-01
python3 -m http.server
build.py creates disposable projections.
Current build targets:
local → https://cx22.localhost/cygnus_x1/labs/KSN-01/
public → https://cx22.ns-x.net/cygnus_x1/labs/KSN-01/
Outputs include bookmarklet and browser-extension versions.
The bookmarklet projection simply flattens newlines. It is not a JavaScript minifier.
Do not edit generated files in dist/.
Dependencies
Default stance: none.
Prefer:
- browser APIs
- vanilla JavaScript
- Python standard library
- HTTP
- JSON
- static files
Avoid introducing:
- npm
- frontend frameworks
- bundlers
- databases
- application servers
- cloud services
- analytics/telemetry
- bespoke protocols
A dependency must solve a demonstrated problem that is unreasonable to solve with existing platform primitives.
Browser clients
Bookmarklet and browser extension are projections of the same core JavaScript.
Future Safari/iOS packaging should remain another projection, not a separate implementation.
Platform-specific packaging is acceptable. Forking core annotation behavior by platform is not.
Scope discipline
Do not prematurely design:
- accounts
- feeds
- discovery
- comments
- likes
- synchronization services
- federation
- reputation
- vouching
- key-based friend permissioning
- conflict resolution
- annotation editing UI
- databases
These are future layers.
They may influence today's interfaces, but they should not become today's implementation until the current brick requires them.
Working rule
When considering a change, ask:
What is the smallest change that proves the next behavior while keeping files, protocols, and implementations replaceable?
Prefer one understandable function or file over a new subsystem.