Runtime
Hosted Responsibility Boundaries for Python, Browser TypeScript, Server TypeScript, and Rust
Report summary
[Proposal] The best default architecture is a browser TypeScript shell plus a server TypeScript modular monolith , backed by PostgreSQL and object storage , with a narrow Rust worker only for hostile-input verification if plugin/package uploads launch in phase one. Python should be deferred unless y
Key topics
- Runtime
- AI
- .NET
- TypeScript
- Python
- Rust
- Semantic Systems
- Research Archive
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
Source availability: 25 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
Executive summary
[Proposal] The best default architecture is a browser TypeScript shell plus a server TypeScript modular monolith, backed by PostgreSQL and object storage, with a narrow Rust worker only for hostile-input verification if plugin/package uploads launch in phase one. Python should be deferred unless you later prove that reporting, content pipelines, batch enrichment, or ML-adjacent workflows materially benefit from its ecosystem enough to justify another runtime. This recommendation keeps the smallest operable shape, aligns the high-concurrency web control plane with an evented server runtime, keeps browser credentials out of browser storage through a BFF/session model, and uses Rust only where compile-time memory/concurrency guarantees are unusually valuable for untrusted parsing or cryptographic verification.
[Proposal] The design center should be: one authenticated web application deployment, one async worker deployment, one database, one immutable blob store, and one public/static site path that can remain available even if authenticated services degrade. The enrolled C# desktop remains the final authority for local actions; the hosted estate may accept, authorize, persist, and deliver remote intents, but it must never become a direct browser-to-operating-system control path.
| Decision | Recommendation |
|---|---|
| Adopt now | Browser TypeScript for the app shell and accessibility-first UI; server TypeScript for BFF, sessions, remote coordination control plane, memory APIs, admin, and most web workflows |
| Adopt now if plugin uploads ship now | Narrow Rust verifier worker for untrusted package parsing, manifest validation, signature/provenance checks, and hostile-input resource isolation |
| Defer | Python runtime, managed broker/stream platform, multi-service decomposition, custom identity service |
| Reject | Browser token storage, browser-to-OS authority, shared database tables as undocumented cross-application APIs, public web processes executing uploaded plugins |
| Revisit when measured | Python workers for data/content/reporting, Rust hot-path services beyond verification, separate realtime gateway, separate databases per service |
[Proposal] If the backend team is materially stronger in Python than in server TypeScript, and the initial product has light realtime fanout, a Python modular monolith is still a defensible alternate. It is not the default here because the early hosted estate is dominated by I/O-heavy coordination, session handling, and long-lived connections rather than CPU-heavy computing.
Scope and method
[Assumption] This report accepts the owner-supplied fixed facts: the Windows desktop remains C#/.NET; browser TypeScript and server TypeScript are different trust/deployment contexts; hosted services may coordinate authenticated remote requests only after enrollment and explicit remote enablement; the local desktop policy engine remains the final authority for local actions; local desktop features must continue to work without a hosted account; and unknown product internals remain unknown. No product access, credentials, logs, traffic captures, source code, infrastructure, or database schemas were used.
[Proposal] The decision method is weighted toward the things that are hardest to fix later: trust boundaries, operational simplicity, data ownership, rollback safety, and on-call burden. A useful starting weight set for this product is: security/trust boundary integrity 25, operability and rollback 20, I/O concurrency and connection handling 15, ecosystem maturity 10, developer velocity 10, data/transaction fit 10, staffing and hiring realism 5, and raw compute efficiency 5. That weighting intentionally resists “technology enthusiasm” and pushes new runtimes behind measurable need rather than aesthetic preference.
| Source priority | Why it matters | Examples used |
|---|---|---|
| [Protocol requirement] Standards and RFCs | Session, token, PKCE, native-app, device-flow, and replay rules should come from protocol authors, not blog summaries. | RFC 7636 PKCE, RFC 9700 OAuth 2.0 Security BCP, RFC 8252 native apps, RFC 8628 device grant, RFC 9449 DPoP, RFC 6265 cookies. |
| [Platform fact] Runtime and database documentation | Concurrency, worker/thread tradeoffs, storage semantics, and observability signals should come from the platforms themselves. | Node.js event-loop and worker docs, Python asyncio/multiprocessing docs, TypeScript handbook, Rust book, PostgreSQL transaction/constraint/UPSERT docs, OpenTelemetry docs. |
| [External guidance] Security and supply-chain guidance | Application security, testing, and software update trust benefit from cross-industry guidance and open standards. | OWASP ASVS, WSTG, session/forgot-password/secrets guidance, TUF, Sigstore, in-toto, SLSA, SPDX, CycloneDX, W3C accessibility guidance. |
[Proposal] Every externally grounded consequential claim below is tagged as one of: Protocol requirement, Platform fact, External guidance, Security synthesis, Distributed-systems synthesis, Proposal, Assumption, or Unknown. Where the evidence is architectural rather than normative, the claim is explicitly marked as synthesis or proposal.
Runtime placement
Decision criteria and workload scorecard
[Proposal] The hosted estate should be placed by workload characteristics, not by language loyalty. The key questions are: Where does untrusted input cross a trust boundary? Where are connections long-lived? Where do you need hard isolation and independent rollback? Which paths are CPU-bound versus primarily orchestration? Which modules need the fastest product iteration? Which ones would create disproportionate on-call burden if split too early?
| Workload | Browser TS | Server TS | Python | Rust | Recommended default |
|---|---|---|---|---|---|
| App shell, UX, accessibility, retries, offline hints | 5 | 1 | 1 | 1 | Browser TS |
| BFF, sessions, OAuth callbacks, CSRF/CORS/CSP enforcement | 0 | 5 | 4 | 2 | Server TS |
| Device enrollment and remote coordination control plane | 0 | 5 | 4 | 3 | Server TS |
| Long-lived delivery fanout | 0 | 5 | 3 | 4 | Server TS first, Rust only if later measured |
| Memory and agent APIs | 0 | 4 | 4 | 3 | Server TS |
| Admin/support surfaces | 4 | 4 | 4 | 1 | Server TS first |
| Untrusted package parsing and signature/provenance verification | 0 | 2 | 2 | 5 | Rust |
| Batch data enrichment, reporting, content pipelines | 0 | 3 | 5 | 2 | Python when justified |
[Security synthesis] The scorecard favors server TypeScript on the control plane because the early hosted estate is mostly request orchestration, connection management, session handling, and realtime state propagation. It favors Rust only where memory safety, compile-time ownership checks, or predictable CPU efficiency are directly relevant to hostile inputs or hot paths. It holds Python in reserve because its main strengths are library depth and iteration speed for data-heavy or batch-heavy workloads, not because it is weak in web services. Those are different decisions.
Runtime profiles
[Platform fact] Browser TypeScript should own rendering, accessibility, progressive enhancement, optimistic UI, retry UX, and non-authoritative local state. TypeScript’s role is static verification for large JavaScript codebases, not runtime trust enforcement. W3C frames accessibility as designing sites so people with disabilities can perceive, understand, navigate, and interact with the web, which makes the browser shell the right place to concentrate accessibility work. CSP, SameSite cookies, and browser storage rules also make it clear that browser code is a hostile but necessary presentation layer, not a trust anchor.
[Security synthesis] Browser TypeScript must never be trusted for authorization, tenant checks, policy enforcement, secret custody, durable audit truth, or any action that can change local desktop state. The browser can hide unavailable actions for usability, but the server must re-check every authorization rule. It should also avoid storing session tokens, refresh tokens, JWTs, or other credentials in localStorage or sessionStorage; OWASP explicitly recommends keeping those out of Web Storage because any JavaScript executing in the origin can read them.
[Platform fact] Server TypeScript is strongest where the workload is I/O-heavy, session-centric, and closely coupled to frontend behavior: BFF endpoints, route handlers, OAuth callbacks, SSE/WebSocket fanout, and coordination APIs. Node’s guidance is explicit that you should not block the event loop, and its worker_threads documentation is explicit that workers are mainly for CPU-intensive JavaScript and do not help much with I/O-intensive work. That maps unusually well to a web control plane whose core responsibility is “accept, authorize, persist, deliver, observe,” not “parse hostile binaries in-process.”
[Security synthesis] Server TypeScript’s main risks are event-loop stalls, large package-dependency exposure, and the tendency to confuse compile-time TypeScript safety with runtime validation. Because TypeScript is a static typechecker for JavaScript, trust-boundary payloads still need runtime schema validation before business logic or persistence. Node’s own security guidance also highlights application-level threats such as denial of service and malicious third-party modules, which argues for aggressive dependency minimization and clear package-allow policies.
[Platform fact] Python remains a strong hosted option for mature web frameworks, administration, content pipelines, and asynchronous/background work. The Python docs describe asyncio as a foundation for multiple asynchronous frameworks, and multiprocessing as a way to side-step the GIL by using subprocesses instead of threads. That makes Python a good fit for “workflow plus libraries” problems, especially when you need fast iteration over data or content-handling tasks.
[Security synthesis] Python’s risks are not “Python cannot scale”; they are more specific: CPU-bound work needs process isolation; dynamic typing increases boundary discipline requirements; and serialization-heavy pipelines can become expensive if you split too early. In this product, those facts push Python into a deferred role, not a rejected one: bring it in if plugin analysis, reporting, search enrichment, content ingestion, or operational automation become library-heavy enough to win back the cost of another runtime.
[Platform fact] Rust is justified when the hosted estate crosses a hostile-input boundary or a measured hot path. The Rust book states that ownership rules are compiler-checked and impose no runtime slowdown, and its concurrency chapters describe many concurrency errors being caught at compile time rather than production runtime. That is unusually valuable for a narrow plugin/package verifier, signature/provenance checker, or input-normalization worker that must process attacker-controlled bytes safely.
[Proposal] The evidence threshold for Rust should be concrete and narrow: either security boundary need or measured operational pressure. Adopt it immediately for hostile package parsing and cryptographic verification if plugin uploads ship now. Otherwise require proof such as sustained CPU saturation, queue backlogs caused by compute-heavy transformations, or p99 latency/cost pressure that the server TypeScript monolith cannot relieve with simpler extraction.
Bounded contexts and starting topology
Responsibility assignment
[Proposal] The hosted estate should begin as a modular monolith with explicit bounded contexts. Modules are business boundaries first, future service boundaries second. The goal is to avoid duplicated business logic across languages and to stop shared database tables from becoming the real undocumented integration surface.
| Bounded context | Authoritative owner | Allowed callers | Data owner | Runtime | Prohibited coupling |
|---|---|---|---|---|---|
| Identity and account | Identity/BFF session module | Browser, desktop callback, admin | identity and account profile tables | Server TS | No direct writes from other modules; no browser token custody |
| Tenant and membership | Tenant module | Identity, remote coordination, memory, admin | tenant and membership tables | Server TS | No UI-only authorization shortcuts |
| Device enrollment | Enrollment module | Desktop, browser account UI, admin | device and enrollment tables | Server TS | Website never executes local actions |
| Protected remote coordination | Remote control-plane module | Browser UI, desktop transport, audit/admin | remote request, delivery, result tables | Server TS | No browser-to-OS authority |
| Memory and agent coordination | Memory/agent module | Browser UI, workers, admin | memory, agent message, retention tables | Server TS first | No silent expansion into local desktop authority |
| Plugin catalog and submission | Plugin control-plane module | Browser UI, verifier worker, reviewers, desktop read path | submission, catalog, review tables | Server TS + Rust verifier | Public web process must not execute uploads |
| Notifications/email | Notification worker | App modules via outbox | notification tables and outbox | Server TS first | No SMTP/API secrets in repos or broad env dumps |
| Public site and docs | Public site module | Anonymous browsers, crawlers | static content/blob store | Browser TS + static hosting | No hard dependency on auth/control-plane uptime |
| Audit and administration | Audit/admin module | Internal admins, workers | append-oriented audit tables/views | Server TS | Application tables are not audit logs |
[Distributed-systems synthesis] Only one module should own each authoritative dataset and schema migration stream. Other modules may call its internal interface, read its published projection, or consume an append-oriented event, but they should not directly mutate its tables. That rule matters even before any service split; otherwise extraction later becomes a risky archaeology exercise rather than a controlled migration. PostgreSQL’s transactional semantics and unique-constraint features are strong enough to support this ownership model from the start.
Starting topology
flowchart TD
Browser["Browser TS app shell"] --> BFF["Server TS BFF + modular monolith"]
Docs["Static docs/public site"] --> Browser
BFF --> PG[("PostgreSQL")]
BFF --> Blob[("Object storage")]
BFF --> Outbox["Outbox table"]
Outbox --> Worker["Server TS worker"]
Worker --> Mail["Email provider"]
Worker --> Blob
BFF --> VerifyQueue["Verification jobs"]
VerifyQueue --> Rust["Rust verifier worker"]
Rust --> Blob
Rust --> PG
Desktop["Enrolled C# desktop"] <--> Channel["SSE / WebSocket / poll channel"]
Channel <--> BFF
Desktop --> Policy["Local desktop policy engine"]
Policy --> Desktop
[Proposal] The minimum operable deployment is: one authenticated application deployment, one worker deployment, one PostgreSQL instance, one object store, and one static/public surface. Public documentation should be statically renderable or cacheable so it remains available if the authenticated control plane is degraded. The current IETF browser-app guidance also treats serving static frontend code as a separate responsibility from handling OAuth interactions, even if both are ultimately deployed together.
Extraction criteria
[Proposal] Service extraction should be earned. The following triggers are synthetic but concrete enough to guide decision records:
| Trigger | Extraction | Why |
|---|---|---|
| Event-loop lag or handler CPU regularly harms user-facing p95/p99 latency | Move that CPU-heavy function to a worker; use Rust if the work is hostile-input or compute-bound | Protect the control plane and keep rollback simple |
| Plugin verification requires seccomp/network-denied isolation, large timeouts, or crash-only containment | Keep or extract a dedicated verifier worker | Security boundary, not language preference |
| Docs/public site deploy cadence diverges from auth/control-plane deploy cadence | Split static/public site delivery from authenticated app | Keep docs available during auth incidents |
| Remote delivery fanout or connection counts become a distinct scaling problem | Extract a dedicated realtime gateway | Separate long-lived connections from request/response app capacity |
| Reporting/content/data enrichment becomes library-heavy and batch-dominant | Add Python workers | Exploit Python’s ecosystem without moving the core control plane |
[Proposal] The rollback rule should be equally explicit: every extraction must preserve an in-process fallback path or a feature-flagged bypass until the new unit has passed contract, load, and incident-drill evidence.
Identity, sessions, and protected remote coordination
Account and identity architecture
[Proposal] Use centralized identity as a boundary, but do not build a custom identity service first. The practical shape is a standards-compliant IdP plus one BFF/session boundary in the application. Multiple related sites should normally share identity and perhaps a narrow account-profile service, not raw auth tables and not a “shared auth library” that quietly turns database rows into a public API.
[Protocol requirement] For browser-based applications, the current best practice is authorization code flow with PKCE, and the current IETF browser-app guidance treats BFF and token-mediating backend patterns as first-class architectures for browser apps. That matters here because a BFF lets the server act as the confidential OAuth client and lets the browser use session cookies rather than durable token storage.
[Protocol requirement] For the desktop, prefer the system browser and authorization code with PKCE. RFC 8252 recommends native apps use an external user-agent, and RFC 8628 is explicit that device authorization is meant for input-constrained or browser-limited devices and is not intended to replace browser-based OAuth in capable native apps. If the desktop is treated as a public client, PKCE is non-negotiable, and DPoP is worth considering if the IdP supports sender-constrained tokens well.
[External guidance] Browser sessions should use HttpOnly, Secure, and explicit SameSite cookies; session identifiers should rotate after authentication and any privilege change; and password reset should use single-use, cryptographically strong, time-limited tokens delivered over a side channel with anti-enumeration and rate-limiting protections. OWASP’s session and forgot-password guidance is clear on each of those points.
[Proposal] Service-to-service authentication should use short-lived workload identity from a secret provider or platform identity plane wherever possible. Avoid static credentials in repositories, broad environment dumps, wiki pages, screenshots, or build artifacts. Centralized secret storage, rotation, least privilege, and break-glass procedures should be present from the beginning, even if the deployment count is small.
Delivery channel and trust boundary
[Security synthesis] The remote-control design should separate: the control plane that accepts and authorizes a remote request, the durable request state that records intent and status, the delivery channel that notifies or contacts enrolled desktops, the local desktop policy engine that makes the final allow/deny decision, the execution path inside the desktop, and the result/audit path that reports back. That separation is the reason “website-side dispatch coordination” is acceptable while “direct browser-to-OS authority” is not: the website coordinates, but the desktop authorizes and executes locally. The browser remains a requester, not an executor.
sequenceDiagram
participant U as User in browser
participant W as Web app / BFF
participant DB as Durable request store
participant C as Delivery channel
participant D as Enrolled desktop
participant P as Local policy engine
U->>W: Submit remote intent
W->>W: Authorize account, tenant, device
W->>DB: Persist intent + idempotency key
W->>C: Arrange delivery
C-->>D: Notify or await reconnect
D->>DB: Fetch pending intent envelope
D->>P: Evaluate local policy
alt locally denied
P-->>D: Denied
D->>W: Signed denial/result
else approved
P-->>D: Approved
D->>D: Execute locally
D->>W: Signed status updates + final result
end
W->>DB: Append audit history / update state
U->>W: Poll or subscribe for state
W-->>U: queued|delivered|approved|denied|executing|completed|failed|cancelled|expired|unknown
[Platform fact] For the delivery channel, the default ordering should be: SSE for simple server-to-client status streams, WebSockets only when true bidirectional low-latency traffic is required, and polling as the lowest-common-denominator fallback. MDN notes that SSE reconnects by default when the connection closes; MDN also notes that the classic WebSocket API has no built-in backpressure support; and MDN marks Background Sync as limited-availability, which makes it a poor dependency for critical workflow semantics.
[Proposal] The honest user-visible state machine should include: queued, delivered, locally approved, denied, executing, completed, failed, cancelled, expired, and outcome unknown. “Outcome unknown” is important: it is the only honest state after channel loss where the control plane cannot yet prove whether the desktop executed or merely received the request.
Data, supply chain, operations, and testing
Data ownership, transactions, and messaging
[Proposal] Start with one PostgreSQL database and strict schema/module ownership. Use one migration stream per owning module, and forbid direct cross-context writes even inside the same database. In a monolith, “shared database” can be safe; “shared-write free-for-all” cannot.
[Platform fact] PostgreSQL’s transaction model gives exactly the primitives needed for authoritative state transitions: all-or-nothing writes, invisible intermediate states, and durable commit semantics. PostgreSQL also gives you unique constraints and INSERT ... ON CONFLICT semantics for idempotency and deduplication, including atomic UPSERT behavior under concurrency. That is enough to support request-state transitions, transactional outbox records, inbox dedupe, and reconciliation without a broker on day one.
[Distributed-systems synthesis] Use this progression: relational write model first, outbox second, worker polling or LISTEN/NOTIFY style wakeups if useful, managed queue only when you truly need independent scaling, retention/replay, or cross-deployment fanout beyond what a database-backed queue can comfortably carry. This product’s early async workloads—email, verification jobs, status fanout, memory indexing—do not justify a Kafka/NATS/Rabbit-first posture by default.
Plugin pipeline and software supply chain
[Proposal] The plugin pipeline should be split cleanly into: upload intake, immutable artifact storage, quarantine, manifest validation, signature/provenance verification, isolated static or malware analysis, reviewer workflow, catalog publication, staged rollout, revocation, and transparency history. The public web process must never import or execute uploaded packages.
[External guidance] The supply-chain standards are complementary, not interchangeable. TUF protects update systems even when repositories or signing keys are compromised; Sigstore provides signing plus transparency-log-backed verification; in-toto records what steps were performed, by whom, and in what order; SLSA structures progressive provenance and build assurance; SPDX and CycloneDX provide machine-readable inventory formats, with CycloneDX also covering services, dependencies, vulnerabilities, and declarations. That stack supports a credible plugin publication story without requiring every control on day one.
[Proposal] A sensible staged adoption is: immutable artifact digests, quarantine, SBOM capture, signer verification, isolated verifier jobs, review separation, staged rollout, emergency revocation, and desktop trust verification. Add transparency history, richer provenance policy, and stricter channel promotion over time.
Security, observability, and fitness
[External guidance] The security baseline should be ASVS for requirements coverage and WSTG for testing coverage. The threat model must explicitly include account takeover, cross-tenant access, CSRF, XSS, SSRF, replay, device impersonation, confused deputy behavior, queue poisoning, malicious package upload, dependency compromise, insider misuse, and audit tampering. CSP, SameSite cookies, strict session rotation, and anti-enumeration password reset controls are all baseline web hygiene, not optional hardening.
[Security synthesis] Rust can reduce memory-unsafety risk in hostile-input workers and some concurrency classes of bugs. It cannot repair flawed authorization, incorrect tenancy rules, insecure recovery design, weak audit models, or confused-deputy control-plane mistakes. Those are product-policy and protocol-design problems first.
[Platform fact] Observability should use standard signals: traces for the request path, metrics for runtime measurements, and structured logs for durable event records. OpenTelemetry’s guidance is also clear that structured logs are preferred in production and that high-cardinality metric attributes can cause unbounded memory growth. That argues for correlation IDs across browser, server, worker, and desktop boundaries, plus careful attribute discipline.
| Example SLI | Why it matters |
|---|---|
| Login success rate and auth callback latency | Detect IdP/session regressions |
| Device enrollment completion rate | Capture onboarding friction |
| Remote intent acceptance latency | Control-plane responsiveness |
| Delivery-to-desktop within TTL | Coordination health |
| Result retrieval completion rate | End-to-end request integrity |
| Memory submission success and indexing lag | Hosted memory/agent health |
| Plugin verification turnaround | Submission pipeline health |
| Email dispatch delay | Recovery/notification reliability |
[Proposal] Testing should be organized around seam quality, not just framework count: unit tests for parsers, validators, state machines, mappers, retry classifiers, repositories, and authorization checks; contract tests and golden vectors shared across C#, TypeScript, Python, and Rust; deterministic fakes for identity, email, storage, queueing, clocks, randomness, and secret providers; browser and accessibility tests for critical UI paths; fuzzing/property tests for hostile-input verifiers; and architecture-fitness rules that fail builds on forbidden browser/server imports, direct cross-domain table writes, token storage in browser storage, secrets in artifacts, and incompatible contracts. ASVS and WSTG provide the external testing spine; the rest is product-specific enforcement.
Migration roadmap, open questions, and deliverables
[Proposal] The migration path should be phased, not revolutionary:
| Phase | Adopt now | Defer | Exit criteria |
|---|---|---|---|
| Foundation | Browser TS shell, server TS modular monolith, PostgreSQL, object storage, BFF auth, outbox worker | Python, broker, extra services | All core flows green in CI, basic runbooks and backups proven |
| Trust hardening | Rust verifier worker if plugin uploads launch; strict audit and revocation flows | Realtime gateway extraction | Hostile-input boundary isolated and tested |
| Scale by measurement | Split public/static site delivery; add dedicated realtime gateway only if fanout becomes distinct | Per-context databases | SLO pressure or rollback coupling proves need |
| Specialized workloads | Add Python workers for reporting/content/data enrichment if library depth wins | Rewrite core control plane | Measured productivity or capability gain outweighs runtime tax |
[Unknown] The biggest unanswered questions are not language questions but product questions: expected concurrency for enrolled desktops, whether plugin uploads ship in phase one, the true memory/agent workload shape, regional/compliance constraints, preferred identity provider, required incident-response evidence, and whether public docs must survive full auth/control-plane outage. Those should be resolved through authorized internal validation, not guessed from architecture diagrams.
[Proposal] The requested return package has been prepared here:
- report.md
- source-ledger.json
- workload-placement-matrix.csv
- responsibility-matrix.csv
- open-questions.md
- reference-artifacts directory
[Limitations] This report is intentionally decision-ready but non-forensic. It does not claim any product-observed behavior, measured throughput, actual schema shape, infrastructure topology, or production failure mode, because none of those were available under the assignment’s zero-access rules.