Self-hosted service
DaKheera47/job-ops avatar
DaKheera47/job-ops

job-ops' licence bars paid hosting and the project sells a paid hosted tier

job-ops: DevOps principles applied to job hunting. A self-hosted pipeline to track, analyze, and assist your application process

3,993 stars528 forksTypeScriptNOASSERTION

At a glance

What is it?
A self-hosted job application pipeline that scores roles, rewrites a CV and watches Gmail, where the stated terms allow self-hosting and modification but forbid selling the software or running a paid hosted service built on it, and the root manifest carries no version at all.
Who is it for?
job-ops suits one person tracking their own applications, who wants their CV data and their search history to stay on their own machine and who already has a choice of model provider. Check six things first.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The stated terms forbid paid hosting, and a paid hosted tier is for sale

The licence section is short and specific. It names the AGPLv3 together with the Commons Clause, and it spells out the consequence: you may self-host, use and modify the tool freely, and you may not sell the software itself or offer a paid hosted service whose value substantially comes from it. A LICENSE file sits at the root of the tree. What the recorded licence identifier says is a third thing, and it says nothing, so the file's own statement and the recorded licence identifier do not line up, and there is no way to tell which one governs. Practically, the restriction binds third parties rather than the party that wrote the terms, and the project itself sells a hosted tier at thirty pounds a month with a Stripe link on the page. For a reader the practical consequence is narrow and clear: you can run it for yourself and for your employer, and you cannot build a paid hosted product on top of it.

The root manifest has no version field and delegates versioning to a script

The root package manifest is marked private, which is right for a workspace root, and then it has no version key at all. Versioning lives elsewhere: there is a script named for setting the orchestrator's version, which runs a small script in the scripts directory, and the version a user ends up running is whatever that script last wrote into a child workspace. So the file a reader would open first to answer the question contains no answer to it. The release record makes the consequence stranger. The three newest tags carry end-user changelog titles rather than conventional messages, one of them reading as a hotfix for a permissions problem, another advertising larger watchlists and more model providers, and a third named after a single board fix. The line is still at zero and thirteen after all of that, which for a self-hosted tool means the tags are the only versioning signal and they are not in the manifest. The published container image is tagged with a moving latest label, so it is not a version pin either.

Two of the model routes are local command line tools, not API keys

Eight model providers are listed and they are not eight of the same thing. Several are ordinary hosted APIs where you paste a key. The others are local executables that the application spawns as a subprocess, and the difference matters for cost and for consent. One is described as a Codex app-server running inside the container, authenticated by running a login command, with its home directory and session data persisted in a named volume. Another is a Claude Code route, and the environment example is unusually candid about it: the setup instructions say to run a token setup command and paste the printed token, and that doing so requires a Pro, Max, Team or Enterprise subscription, with an API key named as the alternative. So one route bills you per token through an API and another draws down a flat subscription you already pay for, and the same feature, rewriting a CV, is charged to two different places depending on which provider you configure. The container pins both command line tools to exact versions as build arguments, so the copies your instance drives are fixed even though your own machine's are not.

A TypeScript orchestrator that shells out to Python for at least one board

The project is described as TypeScript and the manifest is a Node workspace, but one of its dependencies is a Python library, credited in the acknowledgements as powering one of the extractors. The container makes that dependency explicit: the base image installs a Python interpreter, its minimal standard library package and the pip installer, and the compose file sets a path variable pointing at the system Python with a comment noting it uses the system Python in the container. So the runtime image carries a second language for the sake of a single extraction path, and the application is expected to find that interpreter at a fixed absolute location. The board table makes a second, related point. Twelve boards are scraped directly, and one Australian board is marked as going through a separate hosted scraping service instead. That is the only row with a third party in it, and it means for that one board the data your pipeline sees depends on somebody else's availability and rate limits rather than on your own scraper.

Two typesetting engines behind one CV renderer, and one of them has a documented rate limit

Export produces a PDF locally or hands the document to an unrelated service. The local path is not one engine but at least two. The compose file persists a cache volume for a LaTeX engine, and the comment above it is the most useful sentence in the file: without that cache, every time the container is recreated it re-downloads the support bundle on the first render, which is slow and can fail with rate limiting. That is a self-reported failure mode with a named cause and a named symptom, and the volume is the fix. Alongside it, the manifest has a pair of scripts for generating and validating a theme for a different typesetting system, and the validation script runs a single named test file in the resume renderer. So the renderer supports both engines. What the compose file does not do is cache the second one, which means a deployment that uses the theme path pays the same first-render download that the comment warns about, for a different engine.

The compose service builds from source, names a published image, and wants a secret

The quick start is three commands, and the third is the whole installation:

bash
git clone https://github.com/DaKheera47/job-ops.git
cd job-ops
docker compose up -d

Then you open a port on the host and follow a wizard. The compose file behind that command is doing more than the three lines suggest. The service declares a build context, so compose compiles the image locally rather than only fetching one, and it also names a published image from a container registry with a moving latest tag, so the same service can be run either way and the file does not say which one the quick start gets. It declares a build secret, and that secret is a token for a code host, which the quick start never mentions and the guided walkthrough might cover. The port mapping is worth noting because it is not the same number on both sides: the host publishes 3005 and the container listens on 3001. Three volumes are mounted, one for the database and generated documents, one for the authenticated session described earlier, and the typesetting cache.

The analytics opt-out is a firewall rule and the headline numbers are self-reported

The analytics section is two sentences. The tool includes anonymous usage analytics through a named third-party service, and the way to opt out is to block that service's hostname in a firewall or in DNS. There is no environment variable for it, no flag, and nothing in the example environment file, which is otherwise dense with provider keys, timeouts, binary path overrides and a trust-workspace switch. So a self-hoster who reads the configuration reference will not find a telemetry setting, and the only documented control is a network-level one. That is a defensible choice, and it is also a choice that leaves the default on for anyone who does not read that section. The other thing in this file that deserves a flag is the header. It leads with a user count, a count of searches run, and a claim about a position on a trending list for one language. None of the three carries a date or a method, and a trending position is a snapshot that decays, so treat them as marketing rather than as measurements.

The root manifest delegates every tool to a child workspace

Four development tools are declared in the root manifest, and not one of them runs the checks. The lint and format commands are given as a relative path into another workspace's installed binaries, so the repository's own style gate depends on a child workspace's dependencies having been installed first, even though the configuration file for that tool sits at the root. The test command delegates to a script in the orchestrator workspace, and the type check runs two child scripts in sequence. Type checking is where the shape becomes visible: there are thirteen boards in the table, all of the extractors are workspaces, and the root manifest carries dedicated type check scripts for exactly two of them, the United Kingdom visa board and the graduate board. Eleven have no equivalent entry point. The root also carries configuration for four different coding agents, a model context protocol config, a browser automation directory, and a skills lock file, which is a wider tooling surface than the application it ships.

Editorial conclusion

job-ops suits one person tracking their own applications, who wants their CV data and their search history to stay on their own machine and who already has a choice of model provider. Check six things first. The stated terms bar you from offering a paid hosted service built on it, so read the licence before considering a commercial deployment, and note the recorded licence identifier is undetermined even though the file names one. The root manifest has no version field, so nothing in it tells you what you are running. Two of the model provider routes are not API keys at all but local command line tools, one of which consumes a paid subscription rather than a billed API call, and the authentication session for the other is kept in a container volume. The analytics opt-out is a firewall rule rather than a setting. And the compose service both builds from source and names a published image, while requiring a build secret the quick start never mentions.

Frequently asked questions

What is job-ops?

A self-hosted pipeline for tracking and assisting a job application process, written in TypeScript. It searches a list of job boards from one screen, ranks each role against a profile, generates a rewritten CV per role, and connects to Gmail to detect replies, with the file stating explicitly that it does not apply to jobs on your behalf.

What license is job-ops under?

The file states the AGPLv3 together with the Commons Clause, and a LICENSE file is present at the root, while the recorded licence identifier for the repository is undetermined. The stated terms allow self-hosting, use and modification, and bar selling the software or offering a paid hosted service whose value substantially comes from it.

Which AI providers can job-ops use?

Eight listed, mixing hosted APIs with local command line tools. The command line routes include a Codex app-server in the container authenticated by a login command, and a Claude Code route that the environment example says requires a paid subscription and a token from a setup command, rather than an API key.

How do I install job-ops?

Clone the repository, change into the directory and run docker compose up -d, then open http://localhost:3005 and follow the onboarding wizard. The service is also published as a container image, and the compose file declares a build secret that the quick start does not mention.

Does job-ops send usage analytics?

It includes anonymous usage analytics through a third-party service, and the only documented way to opt out is to block that service's hostname in a firewall or in DNS. There is no configuration flag for it in the example environment file.

Official sources

  1. DaKheera47/job-ops on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dakheera47-job-ops.svg)](https://hysenlabs.com/projects/dakheera47-job-ops)