§01/Applytide
Product · Platform · Applied AI
Applytide
The browser had become the workaround for a process that needed its own operating system.
A job-application management platform: pipeline, documents, reminders, analytics and interview prep.
- Role
- Product · design · full-stack engineering
- Span
- Aug 2025 — Apr 2026
- Status
- Source archived
- Team
- Solo project · sole creator
The public repository is archived. No live deployment or external usage is claimed.
§02/The 60-second version
The 60-second version
- 01 / What was broken
- Job discovery, tailored documents, follow-ups and interview context were spread across tabs, files, inboxes and memory.
- 02 / The move
- Make capture almost invisible, then give every application one structured history from discovery to decision.
- 03 / My contribution
- I independently defined, designed and built Applytide end to end: browser capture, the React interface, FastAPI services, data modelling, document tooling, infrastructure and the bounded AI layer.
- 04 / What exists as proof
- An archived public repository with a substantial dated history and inspectable browser, frontend, backend and infrastructure code.
§03/The full story
The story
The summary above stands on its own. Open the decision trail below, one chapter at a time.
01/SignalThe tracking is the job
The tracking is the job
Anyone running a serious job search is operating a pipeline: sourcing, qualification, application, follow-up, interview, decision. It has stages, conversion rates, and a cycle time. It is a process, and people run it in a spreadsheet or in their head.
The friction is not finding jobs. It is the accounting around them — which version of the CV went where, what you said in the cover letter, when you promised to follow up, and which applications are actually still alive. That work is boring, high-frequency, and exactly what software is for.
What I got wrong initially: I assumed the painful step was writing applications. It was not. The painful step was capture — getting a posting out of a browser tab and into a structured record before the tab closed. Everything downstream depends on that one moment being frictionless.
02/FrameConstraints that actually bound the design
Constraints that actually bound the design
Two real constraints shaped almost every decision. The first was cost: any feature calling a language model has an unbounded bill attached to it, and a personal project cannot absorb that. The second was trust: this system holds a user's CV, their employment history, and a list of every company they are quietly talking to. That is a small dataset with a very high blast radius.
So security and cost control were not features to add later — they were framing constraints. That is why the build contains things a side project usually skips: HttpOnly cookie sessions rather than tokens in localStorage, TOTP two-factor, revocable session tracking, per-endpoint rate limits, and a hard daily spending cap on AI calls enforced in Redis.
Explicitly out of scope: automated applying. I excluded it because mass-applying optimises application volume while making the surrounding search process noisier.
03/PlanCapture first, because everything hangs off it
Capture first, because everything hangs off it
Since capture was the load-bearing step, the browser extension was built early rather than as an add-on. Getting a posting into the system in one click is the difference between a tool someone uses and a tool someone means to use.
The rest sequenced along the pipeline itself: capture, then the Kanban stages that model the process, then documents, then the reminder and analytics layers that only become meaningful once there is history to reason about. Analytics last is deliberate — a dashboard over three records is decoration.
The architecture was planned as separate services from the start where the boundaries were genuinely different: HTML email rendering is a Node concern, background scheduling is not an API concern, and the reverse proxy is where rate limiting belongs.
04/BuildEight Compose entries, counted by definition
Eight Compose entries, counted by definition
At the audited public commit, Docker Compose contains eight service entries, including development-only MailDev. The runtime topology also includes Nginx, a React SPA, a FastAPI backend, a Node email service, PostgreSQL, Redis and a scheduler process.
The backend is layered rather than flat — 15 include_router registrations at the API boundary, a domain layer holding business logic, 19 SQLAlchemy model class declarations under Alembic migrations, and infrastructure for email, LLM access, security and workers. Those are snapshot counts with recorded definitions, not product outcomes.
The hardest problem was extraction: turning an arbitrary job page into structured fields. The naive answer is to send the page to a model. That answer is wrong for reasons covered in the decision log below.
The second hardest was internationalisation. Full Hebrew support means real RTL — not a mirrored stylesheet, but a layout that is correct in both directions, including inputs, charts, and drag-and-drop. Retrofitting that is expensive, which is why it was designed in rather than bolted on.
05/ProveBudgets, health checks, and the things you cannot unit-test
Budgets, health checks, and the things you cannot unit-test
Some of this was conventional: Pydantic schemas validating every endpoint boundary, parameterised queries throughout, a health endpoint checking both Postgres and Redis.
The source logs LLM usage per call with token counts and account attribution against a configurable daily budget. That is a spend-bounding mechanism; without operating evidence, it is not a production-reliability claim.
Session management is testable in the same spirit: a user can list their active sessions and revoke them. The question that validates it is not “does the endpoint return 200”, it is “can a user who lost a laptop actually recover”.
06/FieldWhat the archive does — and does not — prove
What the archive does — and does not — prove
The public Applytide repository is archived. There is no live deployment or external-usage result presented here.
I stopped when continuing would have meant operating infrastructure, funding model usage and maintaining a security surface — a different commitment from proving the product and engineering decisions.
What the source independently demonstrates is an implemented multi-service architecture, session and budget controls, bilingual UI and a documented set of design decisions. It does not by itself prove production security, reliability or adoption.
§04/The evidence underneath
System & architecture
- 8
- Compose entries
- 19
- Data models
- 15
- API routers
- 2
- Locales
At audited commit; includes MailDev
SQLAlchemy class declarations
include_router registrations
EN / HE, full RTL
Technology
- React 18
- TypeScript
- FastAPI
- PostgreSQL
- Redis
- Docker
- Nginx
- OpenAI API
- Chrome MV3
Decisions & trade-offs
D-01
2025
Three-stage extraction cascade (JSON-LD → DOM → LLM) instead of LLM-only parsing.
- Why
- Some job boards publish JobPosting structured data that can be extracted without a model call. When it is present and well formed, it preserves source fields directly; this project did not benchmark its coverage or accuracy across boards. A model remains the least predictable path for fields such as salary, where a confident error is costly.
- Tradeoff
- Three code paths and three distinct failure modes to maintain instead of one, in exchange for avoiding model cost and extra transformation when structured source data is available. Coverage and precision were not benchmarked.
- Revisit if
- If model cost per extraction falls far enough that the accounting stops mattering, or if structured-data coverage across boards collapses.
D-02
2025
Hard daily LLM budget enforced in Redis, with per-user attribution.
- Why
- Any AI feature exposed to users is an uncapped liability. Logging token usage after the fact tells you what went wrong; a budget stops it.
- Tradeoff
- Users can hit a ceiling and see a degraded experience on a bad day, which is a worse product moment than silently paying. I preferred a bounded bill.
- Revisit if
- If usage patterns become predictable enough to move from a hard cap to per-user quotas.
D-03
2025
JWT in HttpOnly cookies, not tokens in localStorage.
- Why
- The dataset is a user's employment history and a list of companies they are privately talking to. Any XSS in an SPA turns localStorage tokens into a full account takeover.
- Tradeoff
- CSRF becomes a concern that has to be handled explicitly, and cross-origin setups get harder. That is a better class of problem to own.
- Revisit if
- Not without a very good reason.
D-04
2025
Excluded automated mass-applying from the product scope.
- Why
- It optimises the metric the product displays while making the user's actual outcome worse, and it degrades the ecosystem it operates in. A tool for running a search well should not ship a feature that runs it badly at scale.
- Tradeoff
- Gave up the single most obviously marketable capability and the growth story attached to it.
- Revisit if
- No.
D-05
2026-04
Archive rather than operate.
- Why
- Remaining work was operational — infrastructure, absorbing others' AI costs, and maintaining a security surface that deserves real attention. That is a commitment to operate a product, which is a different decision from building one.
- Tradeoff
- No live deployment to show, and no usage metrics. The code, architecture and reasoning remain inspectable.
- Revisit if
- If there is a reason to operate it that justifies the running cost and the duty of care.
Constraint study
Constraint dial
How should an arbitrary job page become structured data?
Move the constraints and see which answer they point at. The decision was never about the best technology — it was about which one survives these two numbers.
What I actually built
The three-stage cascade — JSON-LD, then DOM parsing, then LLM fallback. This is the implemented path: it avoids a model call when usable structured data exists. Coverage and extraction accuracy were not benchmarked.
Real conditions: one-person operating capacity, ongoing cost exposure, and a correctness bar high enough that a wrong salary field is worse than no salary field. That combination points at the cascade.
If I rebuilt it today
- 01
Build the evaluation harness for extraction first. I made routing decisions between the three stages on judgement when a representative labelled corpus would have made them measurable.
- 02
Start with one locale and add Hebrew at a defined checkpoint. Designing bidirectional from day one taxed every component before any external usage was documented.
- 03
Cut the admin panel. It was built because it was interesting, and it served one administrator — me. That effort belonged in capture reliability.