Settings
Crawl behaviour, what counts as a match, the remote-versus-on-site rule, email delivery, and the cron URL that makes the whole thing run by itself.
Projects
Every project starts from the same factory dictionaries, so a new one looks identical to begin with. That is a starting point, not a shared list: edit a phrase in one and the others keep theirs. A dictionary you have never edited stays on the factory phrases, which is why two untouched projects match word for word.
| Project | Database | Items | Sources | Actions |
|---|
What counts as a match
phrase = weight. When a phrase matches, its own dictionary decides what that
match contributes — nothing here is fixed in the code.
SCORE — a count. Add up points per match across the dictionaries
you give points to. By default only the civil-engineering dictionary scores, 1 per
term, so score 5 means five different civil terms appeared and score 0
means none did.
MERIT — the weighted total. Each match adds
sign × factor × weight, doubled when the phrase is in the
title. No ceiling, and it can go negative when only noise matched. This is the figure
worth ranking on.
A penalty dictionary is nothing special — it is simply one with
its sign set to −. Set a dictionary’s merit to off and it still
gates and still decides the work mode, it just stops moving the number.
What each dictionary contributes
| Dictionary | Score / match | Merit uses | × factor | Sign | Title × | Matches | Effect |
|---|
How the two numbers are worked out. Every phrase that matches is counted once, and each one moves the two numbers independently.
Score is the simpler of the two: add up the score / match column, once for every phrase that matched. Nothing else touches it — not the weights, not the title, not the penalties. That is why a noisy page cannot drag the score down, and why minimum score is the gate worth relying on.
Merit is four things multiplied together, for each phrase:
× factor |
straight from that column. |
|---|---|
the weight, or 1 |
Merit uses decides which. “the phrase weight” takes the number you wrote beside the phrase; “a flat amount” ignores it and uses 1, so every phrase in that dictionary is worth the same. “nothing” stops the dictionary moving the merit at all — it still gates, and it still decides the work mode. |
title × |
applied only when the phrase is in the title. Otherwise 1. |
sign |
+ adds it, − takes it away. A penalty dictionary is nothing more than one with the sign set to −. |
So a phrase of weight 5, found in the title, in a dictionary set to
the phrase weight with × factor 2 and title × 4,
contributes 5 × 2 × 4 = 40 to the merit — and 1 to the
score if that dictionary scores at all. The same phrase in the body would be
5 × 2 = 10.
Then two things happen once per listing, which is usually why the per-dictionary rows below do not appear to add up: a live-deadline bonus if the closing date is still ahead, and penalties are trimmed to at most the share of the earned merit set by penalty cap below — half, unless you change it. Noise can dent a good listing; it cannot cancel one.
Work it out on some text
Paste a real listing, or edit the example. It is run through the actual classifier with your saved rules, so the arithmetic below is the arithmetic the extractor uses — it cannot drift out of date the way a written example would.
Your own conditions
The two minimums above judge the totals. These judge what an individual dictionary contributed — “at least two civil terms”, “the noise penalty must not go below −8”. Every line in a group must hold; the block is kept if any one group holds, so groups are alternatives. With no groups written nothing here changes a verdict.
Other rules
Page watching
Keyword dictionaries
phrase = weight. Matching is case-insensitive
and ignores word boundaries, so "phd position" matches inside a longer sentence.One phrase per line, phrase = weight. A line with no
= gets weight 3. That weight is what a match of this phrase adds to the
merit, scaled by its dictionary’s factor; the score
counts the match as 1 whatever the weight. Matching is plain substring and
case-insensitive, so a short phrase catches more than you expect: gis matches
“registration”.
Developer — editing this project with Claude
1. Let Claude read and edit the project
Claude Desktop → Settings → Developer → Edit
Config, and paste this. Claude Code needs none of it — it already has access
to the folder it is started in. Requires Node (winget install
OpenJS.NodeJS.LTS), which supplies npx.
npx is not on your PATH — Node installed under a bundle like
Laragon or XAMPP usually is not registered. There is no error message worth reading when
this happens: the process exits immediately and the connection just closes.
Check with
npx --version in a new terminal. If that fails, either add Node's
folder to PATH, or give the config the full path plus an env block:
"command": "C:\path\to\node\npx.cmd"
"env": { "PATH": "C:\path\to\node;C:\Windows\system32;C:\Windows",
"SystemRoot": "C:\Windows" }
All three parts are needed, and each was found by a separate failure:
npx.cmd starts node by name, so a full path to npx
alone still fails; and if the host replaces the environment rather than adding to
it, Node aborts while starting up without SystemRoot. Adding
TEMP and TMP as well lets npx populate its cache on
a machine where it is empty.
2. Or let Claude edit this live site directly
With this on, changes reach the site as Claude makes them — no
archive, no upload, no File Manager. It works by putting a small endpoint,
mcp-bridge.php, at the top of this install, and giving your computer the only
key to it.
So: it is off until you switch it on, the token is generated here and never stored in the code or carried in an upload,
data/ is refused outright
(that is your password, your databases and your API keys), the endpoint will not rewrite
itself, every write keeps a backup, and eight wrong tokens shut the door for fifteen
minutes.
The alternative, if you would rather not: cPanel’s own Web Disk maps this site as a drive on your computer through cPanel’s login, and
sh deploy/push.sh Z: --live then pushes to it. Same outcome, nothing new
exposed on the server — but Claude cannot drive it, so you run the one command.
3. Or upload the changes yourself
Claude edits the copy on your computer; these send it up. Pick by what your host offers — the first two need nothing at all:
sh deploy/folders.sh |
Builds two folders on D: — one whose contents you drag into
File Manager, one that stays put. Each says which it is. Start here. |
|---|---|
sh deploy/package.sh |
Builds one .tar.gz to upload and Extract in cPanel.
Fewer clicks than dragging a folder tree. |
sh deploy/push.sh Z: --live |
Copies only what changed, onto the site mapped as a drive (cPanel → Web Disk). Needs no SSH, and takes seconds. |
sh deploy/deploy.sh --live |
Over sftp. Needs SSH, which many shared plans do not include — check before
relying on it. Fill in deploy/deploy.config first. |
The first two never upload anything themselves — they only
assemble files on your machine, and you do the upload. The last two do upload, and both
default to a dry run: leave off --live and they only report.
data/ is never uploaded by any of them.
It holds the database — your sources, verdicts, settings and API keys — and
once this is live, the copy that matters is the one on the server. Sending the local one
up would destroy everything collected since the last deploy.
4. After uploading: what is readable over the web?
The .htaccess files shipped here are all that keep
data/ceradar.sqlite — every API key, recipient and verdict —
away from anyone who guesses the URL. They fail silently: a host running
nginx, or Apache with AllowOverride None, ignores them and serves the
database on request, with nothing in any log to say so. This asks the way an outsider
would, one request per path.
.htaccess
that was meant to cover it, shows up here and nowhere else. The endpoint itself refuses
data/, so it cannot expose the database — but it can add files, and a
file added is a file served.
Worth knowing either way: some hosts run a malware scan that flags any script accepting input and writing files, and suspends it without asking. If
mcp-bridge.php
disappears on its own, that is what happened — it is not a bug, and re-uploading it
will not help for long.
Automation
Option A — cPanel cron job. Add this command, hourly:
Option B — web cron / external pinger (cron-job.org, UptimeRobot):
One entry for every project
The two options above run this project only, so each project needs its own entry. Shared hosting is usually short of cron slots, so this single line runs them all instead — one after another, each in its own process, with one token for the whole install.
Option C — cPanel cron job, all projects:
Option D — web cron, all projects:
data/projects.json, because no single project can authorise a job that spans
all of them. Adding a project does not change the line — the new project is
picked up on the next run.
Add
&skip=archive to leave a project out, or
&only=jobs,page-watch to run just some of them.
Who can get in
settings.html
itself, without asking PHP, so this password cannot stop someone fetching one — what
they get is an empty shell, because every value on it comes from behind this password.
Nothing can be read or changed without signing in.
To put the files behind a door too, turn on your host’s own directory protection (cPanel: Directory Privacy) for the install folder — but leave
cron/ out of it, or the scheduled run will be refused before PHP sees its
token.
What is taking up space
By project
| Project | Total | What is inside it | Wasted |
|---|---|---|---|
| Checking… | |||
Outside the databases
- Checking
- …
Free up space in this project
Tick what you do not need. Nothing is removed until you press the button, and nothing here touches your sources, dictionaries or settings.
Keep it under control automatically
RSS-Bridge (optional)
Who else can use it
RSS-Bridge is a separate application in this folder, and the password on this app does not reach it — this install fetches it server-side, with no browser session to hand over. Left open, anyone who finds its address can make your server fetch any page they like, which is the sort of thing a host suspends an account for.
A token closes that. It is generated here and written to
rss-bridge/config.ini.php, which is where RSS-Bridge reads it from —
and that is the only copy. It is not stored as a setting here, because settings belong to
one project while the bridge is shared by all of them, and a per-project copy could only
ever be right for one.
Your saved feeds do not change. The token is added when a feed is fetched, so it is never stored in a source URL, never in a CSV export and never on screen — and rotating it needs no edit to a single saved feed.
One limit worth knowing: RSS-Bridge is server-side PHP like this tool, so it cannot read pages that build themselves in the browser with JavaScript. For those, use the AI reading method instead.
Install it from github.com/RSS-Bridge/rss-bridge.