Skip to content
Sarah Henia.
All work
CED Tunisia

CED Tunisia · graduation internship · Feb to Aug 2026

Collaboris

A real-time presence library for enterprise web apps, with a GDPR-by-design journal and a governed AI assistant on top.

My role
End to end: architecture, implementation, packaging, deployment, executive presentation
Period
February to August 2026, three releases

Stack

  • Angular
  • TypeScript
  • C#
  • .NET
  • SignalR
  • Redis
  • SQL Server
  • Cosmos DB
  • Python
  • FastAPI
  • Azure OpenAI
  • MCP
  • Azure
  • Azure DevOps
  • npm
  • NuGet

On the resume

  • Built Collaboris, a real-time user-presence library for enterprise web apps, shipped as two installable packages: an Angular 19 library (npm) and an ASP.NET Core 8 SDK (NuGet) over SignalR and Redis. Delivered in three releases under department-head review.
  • Presented the architecture, governance and business case to the department head and CED executives.
  • Designed a five-layer identity cascade that recognises users from the host app's existing session, so nobody signs in twice.
  • Built a layered view resolver: a scoped, debounced DOM MutationObserver that locates each user down to the open dialog, tab or wizard step, so co-presence is exact rather than per-route.
  • Detected real user activity by wrapping the browser's fetch and XMLHttpRequest (write methods only, exclusion list, deduplication window) and multiplexed every tab onto one WebSocket with a SharedWorker.
  • Built PresenceAI, the data and AI layer: change-data capture streaming live Redis signals into a Cosmos DB journal (OLTP to OLAP, Python/FastAPI), queried in natural language through a governed Azure OpenAI assistant over MCP.
  • Implemented GDPR compliance by design (data minimisation, consent, 30-day TTL retention, fail-closed access, cost caps) and covered the AI layer with unit tests.
  • Deployed the full stack to Azure (App Service, SQL Server, Azure Cache for Redis) through Azure DevOps build and release pipelines, migrating the API's data layer from PostgreSQL to SQL Server.

The problem

Two claims handlers open the same record in an internal app at the same time. Neither knows the other is there. The first saves. The second, working from what they saw a moment ago, saves too, and silently overwrites the first. No warning, no conflict. The last save wins, and nobody notices until a client does.

Enterprise web apps are blind to their own users. The question was how to make any internal application aware of who is in it and what is changing, without rebuilding it, and without the presence data becoming a surveillance tool.

What I built

Collaboris is a drop-in library: a host Angular application installs an npm package, its backend adds a NuGet package, and one configuration block wires everything. Users see who else is present, down to the open dialog, tab or wizard step, and are told when the data under their eyes changes. Identity comes from the host's existing session; nobody signs in twice.

Release 3 added PresenceAI: the ephemeral presence stream is captured into a durable, retention-bounded journal in Cosmos DB, and allow-listed managers can ask it questions in natural language through a governed Azure OpenAI assistant, also exposed over MCP. Redis holds what is happening; Cosmos holds what happened.

Everything shipped as real packages, installed by the host the way a customer would install them, and deployed to Azure through Azure DevOps pipelines.

How it works

Plays on its own. Click a step to pause.

Decisions and trade-offs

Two stores instead of one
Real time wants microsecond reads and ephemeral state; analytics wants history and aggregates. Redis and Cosmos DB, bridged by a pipeline the live path never waits on.
A deterministic pipeline instead of an LLM-built journal
Cost, reproducibility and trust. The same history always produces the same journal. The model lives only at the edges: classifying questions and phrasing answers.
Session synthesis at write time
Instead of mirroring every snapshot and deleting later, the pipeline decides at ingest what deserves to exist. Storage is bounded by construction, which is also what the GDPR asks for.
An allowlist instead of host role claims for AI access
Role semantics do not travel across countries and applications. Each application's owner declares who may ask, and the default is no.
Host authenticates, library reads
The SDK's own JWT scheme was removed on integration review once it was clear a library cannot own its host's security policy. Removal is a design act.
Cache-augmented generation instead of retrieval
The journal is small by construction, so recent summaries fit in the context window. No vector infrastructure to run.
Python and FastAPI for the AI layer in a .NET shop
Async stream consumption, typed validation, native streaming and the AI ecosystem, as an independently deployable service next to the .NET SDK.

Governance

Data minimisation
Coordinates of work only: identity, location, status, change events. Never content, form values or keystrokes.
Consent
Explicit, informed and revocable, gathered before any tracking; an identity confirmation overlay shows what will be shared.
Retention by schema
A 30-day TTL on fine-grained records, enforced by the database itself. What was never written can never leak.
Surveillance impossible by construction
The journal stores counts, not person-level assessments. Judgment and prediction questions about individuals are refused by design, not by policy.
Fail-closed access and cost caps
No allowlist entry, no access. Per-question and monthly cost ceilings on the AI layer.
Records of processing
Every question, classification, answer and refusal is journaled. The AI layer is covered by unit tests.

What I learned

  • Never trust a component's self-reported health. The hardest bug was a connection that reported healthy while its receive side was dead; the fix was tracking the age of the last received message.
  • A library author controls nothing: not the host's framework habits, storage layout or deployment pipeline. Designing for that is a different discipline from building an application.
  • Benchmark first. Every component began with how the industry already solves the problem, and the day the survey ran out was the day I knew which part of the project was mine.
  • Removal is a design act. A heuristic view engine, a severity ranker, a graph storage path and the SDK's own auth layer were all built, evaluated and deleted, each argued in writing first.

Built inside a private company repository. Names, credentials and internal identifiers are left out on purpose.

Next case study

Market-intelligence system