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

Status
Research archive item
Category
Runtime
Length
3,652 words
Reading time
17 minutes
Report type
evaluation

Key topics

  • Runtime
  • AI
  • .NET
  • TypeScript
  • Python
  • Rust
  • Semantic Systems
  • Research Archive

Research provenance

Archive status
Research archive item
Content identity
sha256:04de90381dc2b01ece45e231d8aec4778a99436a275ac3c4d42478f668c58d19

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.

DecisionRecommendation
Adopt nowBrowser 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 nowNarrow Rust verifier worker for untrusted package parsing, manifest validation, signature/provenance checks, and hostile-input resource isolation
DeferPython runtime, managed broker/stream platform, multi-service decomposition, custom identity service
RejectBrowser token storage, browser-to-OS authority, shared database tables as undocumented cross-application APIs, public web processes executing uploaded plugins
Revisit when measuredPython 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 priorityWhy it mattersExamples used
[Protocol requirement] Standards and RFCsSession, 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 documentationConcurrency, 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 guidanceApplication 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?

WorkloadBrowser TSServer TSPythonRustRecommended default
App shell, UX, accessibility, retries, offline hints5111Browser TS
BFF, sessions, OAuth callbacks, CSRF/CORS/CSP enforcement0542Server TS
Device enrollment and remote coordination control plane0543Server TS
Long-lived delivery fanout0534Server TS first, Rust only if later measured
Memory and agent APIs0443Server TS
Admin/support surfaces4441Server TS first
Untrusted package parsing and signature/provenance verification0225Rust
Batch data enrichment, reporting, content pipelines0352Python 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 contextAuthoritative ownerAllowed callersData ownerRuntimeProhibited coupling
Identity and accountIdentity/BFF session moduleBrowser, desktop callback, adminidentity and account profile tablesServer TSNo direct writes from other modules; no browser token custody
Tenant and membershipTenant moduleIdentity, remote coordination, memory, admintenant and membership tablesServer TSNo UI-only authorization shortcuts
Device enrollmentEnrollment moduleDesktop, browser account UI, admindevice and enrollment tablesServer TSWebsite never executes local actions
Protected remote coordinationRemote control-plane moduleBrowser UI, desktop transport, audit/adminremote request, delivery, result tablesServer TSNo browser-to-OS authority
Memory and agent coordinationMemory/agent moduleBrowser UI, workers, adminmemory, agent message, retention tablesServer TS firstNo silent expansion into local desktop authority
Plugin catalog and submissionPlugin control-plane moduleBrowser UI, verifier worker, reviewers, desktop read pathsubmission, catalog, review tablesServer TS + Rust verifierPublic web process must not execute uploads
Notifications/emailNotification workerApp modules via outboxnotification tables and outboxServer TS firstNo SMTP/API secrets in repos or broad env dumps
Public site and docsPublic site moduleAnonymous browsers, crawlersstatic content/blob storeBrowser TS + static hostingNo hard dependency on auth/control-plane uptime
Audit and administrationAudit/admin moduleInternal admins, workersappend-oriented audit tables/viewsServer TSApplication 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:

TriggerExtractionWhy
Event-loop lag or handler CPU regularly harms user-facing p95/p99 latencyMove that CPU-heavy function to a worker; use Rust if the work is hostile-input or compute-boundProtect the control plane and keep rollback simple
Plugin verification requires seccomp/network-denied isolation, large timeouts, or crash-only containmentKeep or extract a dedicated verifier workerSecurity boundary, not language preference
Docs/public site deploy cadence diverges from auth/control-plane deploy cadenceSplit static/public site delivery from authenticated appKeep docs available during auth incidents
Remote delivery fanout or connection counts become a distinct scaling problemExtract a dedicated realtime gatewaySeparate long-lived connections from request/response app capacity
Reporting/content/data enrichment becomes library-heavy and batch-dominantAdd Python workersExploit 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 SLIWhy it matters
Login success rate and auth callback latencyDetect IdP/session regressions
Device enrollment completion rateCapture onboarding friction
Remote intent acceptance latencyControl-plane responsiveness
Delivery-to-desktop within TTLCoordination health
Result retrieval completion rateEnd-to-end request integrity
Memory submission success and indexing lagHosted memory/agent health
Plugin verification turnaroundSubmission pipeline health
Email dispatch delayRecovery/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:

PhaseAdopt nowDeferExit criteria
FoundationBrowser TS shell, server TS modular monolith, PostgreSQL, object storage, BFF auth, outbox workerPython, broker, extra servicesAll core flows green in CI, basic runbooks and backups proven
Trust hardeningRust verifier worker if plugin uploads launch; strict audit and revocation flowsRealtime gateway extractionHostile-input boundary isolated and tested
Scale by measurementSplit public/static site delivery; add dedicated realtime gateway only if fanout becomes distinctPer-context databasesSLO pressure or rollback coupling proves need
Specialized workloadsAdd Python workers for reporting/content/data enrichment if library depth winsRewrite core control planeMeasured 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.