The Architecture Has to Earn the Trust
Part 1 described the core inversion: personal agents handle integrations, apps focus on presentation, and data lives on the user's device. Part 2 looked honestly at the barriers — the access friction, the two distinct trust problems, and the technical constraints that don't dissolve under enthusiasm. This article is about what you actually build when you take that pattern seriously.
The central point of Part 2 was that trust claims in this space need to be structural rather than rhetorical. Saying "your data stays on your device" only means something if the system is built in a way that makes the alternative architecturally foreclosed rather than merely unlikely or policy-prohibited. An app whose relay server happens not to log content is different from an app whose relay server receives only ciphertext and therefore cannot log content in any meaningful sense, regardless of what someone later decides. That difference is what this article is about.
What follows is a walkthrough of the architecture I'm working through in ScrolLess. It is a live exploration rather than a finished blueprint — the design is in place and the core pipeline is running, but the project is still being built out. I'm documenting it here because the architectural reasoning matters independently of how complete the implementation is.
Five Principles Before the Diagram
Architecture decisions that aren't grounded in explicit principles tend to drift: each individual choice seems reasonable, but the cumulative effect is an incoherent system. These five principles shape every decision in this design.
User-owned data by default. Feed items, read state, preferences, saved content — primary data lives on the user's device. The app reads from local storage. Nothing is held centrally as a condition of using the service. This is not a privacy feature bolted on top; it is the load-bearing assumption everything else is designed around.
Agents handle integrations. The app has no OAuth flows, no API clients, no rate-limit logic, and no knowledge of what Twitter or YouTube look like from the outside. That complexity belongs entirely to the agent layer, which runs independently and is controlled by the user. The app's relationship to the outside world is to receive structured data and render it, and nothing beyond that.
Apps compete on experience. When data ownership is no longer a source of lock-in, the only thing left to differentiate on is how well the app works. This is, I think, a healthier competitive arrangement — interface design becomes the meaningful differentiator rather than the size of your integration catalogue or the switching cost you've engineered into your data model.
Cloud is an optional enhancement. Multi-device sync, reliable background scheduling, and compute-heavy features can be cloud-backed. But the app is fully functional without them. Cloud extends capability; it does not gatekeep it. A user who runs everything locally should get the same core experience as a paying subscriber.
Clear trust boundaries. A user should be able to understand, at a meaningful level of detail, what runs on their device, what leaves it, and what any server in the chain can and cannot access. This is not a UX nicety — it is an architectural constraint. If the relay can read your content, the trust model is broken regardless of what the privacy page promises.
The Full Flow
With those principles in place, the architecture resolves into a clear sequence. The user schedules a sync task. A personal AI agent picks it up, loads its workflow from an MCP server, fetches from the configured sources, structures and deduplicates the results, encrypts the entire payload using the device's public key, and submits it. A relay server receives the ciphertext — seeing nothing it could read even if it tried — and pushes it to the device. The device decrypts, writes to IndexedDB, and the app renders from local storage without a network round-trip.
Two parts of this flow matter because they depart from how most apps are built. The encryption step happens at the agent, before the payload touches any server. This means the relay is blind not by policy but by construction — it routes ciphertext, and the key to decrypt it never passes through it. The device is the terminal point: content arrives, gets decrypted locally, and the app reads it from there. There is no cloud database query standing between the user and their feed.
Deterministic Workflows and MCP's Two Roles
One of the more discussed challenges with AI agents in production is reliability. An agent given a loose instruction like "fetch my YouTube subscriptions and add them to my feed" will behave inconsistently — sometimes working, sometimes hallucinating a field name, occasionally inventing an endpoint that doesn't exist. That is not acceptable for a background task handling real user data on a schedule, and the answer is not a smarter model but a more structured workflow.
Deterministic agent workflows impose structure on what the agent can do and in what order. The application exposes named prompts, typed tools, ordered steps, and explicit schemas, and the agent executes a guided process rather than improvising one. Reliability comes from the design of the workflow, not from trusting the model to reason its way to the right answer every time.
MCP (Model Context Protocol) — now stewarded by the Agentic AI Foundation and increasingly adopted as a standard for agent-tool interaction — plays two distinct roles in this architecture. They're easy to conflate, so the distinction matters.
The first role is as an instruction layer, where the MCP server
is the place the agent learns how to populate the app correctly. It exposes named
workflows — sync_feed, for example — alongside typed tool definitions,
schema specifications, source-specific skill documents, and the user's current
preferences. The agent doesn't need to understand how the app works internally,
because the MCP server tells it exactly what to do and in what shape to deliver
the result. As others exploring MCP's broader implications have noted,
this separation between agent capability and application-specific knowledge is
one of the protocol's most useful structural properties.
The second role is as an ingestion interface. Once the agent has gathered and structured content, it does not push raw results directly to any storage layer; instead, it submits a validated tool call to the MCP server, which acts as the structured boundary between agent output and the application's data layer. This enforces schema compliance and gives the system a single, auditable point where external data enters — a checkpoint rather than an open pipe. One protocol covering both roles, how the agent learns what to do and how the result safely enters the system, is what makes the pattern more than AI with guardrails applied afterwards.
Walking Through sync_feed
The most direct way to see this architecture working is to follow a single
execution of sync_feed — the core scheduled workflow in ScrolLess,
the one that runs on a timer and populates the user's feed. Here is what
actually happens.
The agent begins by calling get_feed_state, which returns everything
it needs before touching any external source: the timestamp of the last
successful sync, the list of enabled sources with their priorities, the device's
public key for the encryption step, and any skip rules the user has configured.
A single call provides the complete context.
With that state in hand, the agent loads the relevant skill resources from
the MCP server — skills/youtube.md, skills/x.md, and
so on for each enabled source. Each skill file is a deterministic set of
instructions written for that specific platform: where to navigate, which
elements to extract, which to skip, and how to handle edge cases like deleted
posts or private accounts. The agent does not improvise these steps; it follows
a document that was written, tested, and tuned for that source.
It then works through each source, applying those instructions. New content
since the last sync timestamp is collected, deduplicated against known
url_hash values, and ranked according to the user's priority
rules. The result is a flat list of items conforming to the app's schema —
same fields, same types, regardless of origin platform. At this point, the
content exists only in the agent's context.
Before anything leaves, the entire payload is encrypted using the device's
public key (ECIES P-256 + AES-256-GCM, with the key returned in the first call).
After this step, the agent has a ciphertext payload suitable for transmission,
and no server in the delivery chain will be able to read it.
The agent then calls write_feed_items with the encrypted payload,
the ephemeral key needed for decryption on the device, and source metadata.
The MCP server validates the structure of the tool call and forwards it to the
relay, the relay pushes it to the device, and the device decrypts, deduplicates
against existing IndexedDB records, and the app reflects the new items on next
render.
If the device is offline when the agent runs, the relay accepts and queues the encrypted payload, then delivers it when the device reconnects. The sync window only advances after successful delivery acknowledgement from the device, so a queued-but-undelivered payload does not silently move the system past content the user has not received.
Where the Data Lives
The device
Feed items, read state, user preferences, saved content, and sync logs all
live in IndexedDB. The app reads from there directly — no network request
required for normal browsing, which gives it genuinely fast local queries and
complete offline support. Deletion is straightforward: clearing the app's local
data clears everything, because everything is there. URL deduplication runs
client-side, checking url_hash values in IndexedDB before storing
a newly delivered item. Data retention is also client-side, a periodic cleanup
against the item's fetched_at timestamp and the user's configured
retention window.
The relay
The relay exists for one reason: to bridge the gap between an agent that may run remotely or on a schedule and a device that may be offline at the time. It receives encrypted payloads and forwards them, stores nothing beyond what is operationally necessary to complete the delivery, and has no knowledge of what platforms the user follows, what the content looks like, or who the user is in any meaningful sense. The payload arrives already encrypted with the device's key and leaves the same way, so the architectural guarantee is not that the operator chooses not to read user data, but that the system makes it infeasible to do so.
The coordination backend
A minimal server handles what the relay can't: push notification subscriptions, device registration, source configuration metadata, MCP server discovery, and — for the optional paid tier — subscription entitlements and multi-device sync metadata. This backend never stores feed content. Its job is coordination, not custody. The distinction between free and paid tiers is handled in the auth middleware; route handlers don't need to know which they're serving.
The Optional Cloud Layer
For users who want more than a single device, an optional subscription tier extends the architecture without altering its character. Multi-device sync, reliable queuing for extended offline periods, background agent scheduling, and compute-heavy features like cross-source summarisation can all be cloud-backed. The constraint that keeps this honest is that these capabilities are strictly additive. A user who never opts in should get a fully functional app — agent running locally or on a machine they control, data on their device, relay handling delivery on a best-effort basis. The paid tier adds convenience and reliability. It does not change what the system can and cannot see.
ScrolLess as a Working Experiment
ScrolLess is the application of all of this — a unified social and news feed aggregator built around this architecture from the ground up. The agent scrapes the platforms the user follows (currently YouTube subscriptions and the X/Twitter following timeline), structures the results into a common content schema, encrypts the payload, and delivers it via the relay. The Preact PWA reads from IndexedDB and renders a chronological feed, and there are no direct connections between the app and any content platform. The feed belongs to the user; the app makes it useful to look at.
The project is in active development, with the core pipeline running across agent scraping, MCP-guided workflows, encrypted relay, and device-side decryption and storage. The architecture described in this series is structural from the first commit rather than something to be retrofitted when the system matures. What's still being built out is the full range of source skills, the preferences and source-management UI, and the optional sync tier. The shell is deployed and the pattern is in place.
The specific decisions that constrain ScrolLess — the blind relay, the device-first storage, the agent's independence from the app — are not incidental. They are the choices that make the trust claims mean something rather than read pleasantly, because a system where the relay operator genuinely cannot access user content is categorically different from one where they merely promise restraint. That difference is harder to achieve and easier to verify, which seemed worth building for.
That's a better arrangement for everyone — except the intermediaries.