Merlion Radar is a Singapore-first, static GitHub Pages job/opportunity radar that anyone can adapt to their own career focus.
It is intentionally not limited to AI security. Configure it for software engineering, data science, product, design, security, research, marketing, leadership, or any mix of roles.
- Pulls public roles from Greenhouse, Lever, Ashby, Remotive, RemoteOK, Singapore's MyCareersFuture portal, and optional custom JSON feeds.
- Scores roles against configurable career signals, locations, title terms, and exclusion terms.
- Publishes a static
/opportunities/page that works on GitHub Pages/Jekyll. - Adds per-role guidance:
- why the role matches
- next action
- skillsets to build
- certifications/courses to consider
- learning gaps to close
- Keeps privacy-safe history keys so trend badges do not require storing raw job URLs in history.
- Runs in GitHub Actions with Python standard library only; no secrets required for public feeds.
- Optional LLM enrichment works with OpenAI-compatible endpoints, Anthropic, Gemini, or any compatible gateway, using environment-variable secrets only.
- Use this repository as a template or fork it.
- Edit
config/opportunity_radar.json:site_urlmax_itemslocation.include_termsrole_profiles- public ATS boards under
sources
- Run locally:
python3 scripts/update_opportunities.py
python3 scripts/validate_opportunities.py- Enable GitHub Pages for the repository.
- The included GitHub Actions workflow refreshes
_data/opportunities.jsonon a schedule and validates it before committing.
Each role_profiles entry defines what you care about:
{
"label": "Data / AI Engineering",
"terms": ["machine learning", "data platform", "mlops", "llm", "analytics engineering"],
"skillsets": ["Production ML systems, data pipelines, model evaluation, and observability."],
"certifications": ["Cloud data/ML engineer certification aligned to your target provider."],
"learning_gaps": ["Show an end-to-end project with data ingestion, training/evaluation, deployment, and monitoring."]
}You can add any domain. The generator treats profiles as scoring dimensions and learning-plan templates.
The radar does not require an LLM or API key. By default, config/opportunity_radar.json has llm.enabled set to false, and the deterministic profile guidance is used.
If you want model-generated guidance, enable the llm block and store the real key outside the repo as an environment variable or GitHub Actions secret. The config should contain only the variable name, never the secret value:
"llm": {
"enabled": true,
"provider": "openai_compatible",
"model": "gpt-4o-mini",
"base_url": "https://api.openai.com/v1",
"api_key_env": "OPENAI_API_KEY",
"max_items_to_enrich": 6
}Supported providers:
openai_compatible: OpenAI, OpenRouter, Together, Fireworks, local gateways, or any/chat/completionscompatible endpoint. Setbase_urlandmodelfor your provider.anthropic: uses/v1/messages; setmodelsuch as a Claude model andapi_key_envsuch asANTHROPIC_API_KEY.gemini: uses Google Generative LanguagegenerateContent; setmodelsuch asgemini-1.5-flashandapi_key_envsuch asGEMINI_API_KEY.
For GitHub Actions, add the key in Settings → Secrets and variables → Actions, then expose it to the generate step, for example:
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}Security guardrails:
- Do not commit literal API keys, bearer tokens, passwords, or private feed credentials.
- The validator rejects token/API-key-looking values and config fields such as
api_key,token,secret, orpasswordwhen they contain values. - LLM output is still validated for required fields and public-data privacy constraints before publishing.
Supported public source types:
greenhouse:{ "company": "Anthropic", "board": "anthropic" }lever:{ "company": "Mistral AI", "slug": "mistral" }ashby:{ "company": "OpenAI", "board": "openai" }remotive: configured with search queries; set to an empty list when you want a Singapore/APAC-only run.remoteok: broad remote feed; set tofalsewhen remote/global jobs should not compete with local Singapore results.mycareersfuture_queries: Singapore portal searches, for example["software engineer", "cybersecurity engineer", "product manager"]; optionalmycareersfuture_limitcontrols rows per query.custom_json: point at a JSON endpoint, an optional local JSON file, or inlineitems, then map fields.
Singapore/APAC tuning options:
location.exclude_termscan block broad non-local matches such asremote,us,remote us,usa,canada, andamericas.source_boostscan lift trusted Singapore/APAC sources such as MyCareersFuture, GovTech Singapore, Airwallex, OKX, or a custom Singapore/APAC feed.source_minimumscan reserve published slots for important local sources so global ATS boards do not dominate the page.
Example custom feed backed by config/custom_opportunities.json:
{
"name": "Custom Singapore/APAC feed",
"path": "config/custom_opportunities.json",
"optional": true,
"items_path": "jobs",
"defaults": {"location": "Singapore / APAC"},
"fields": {
"title": "title",
"company": "company",
"location": "location",
"url": "url",
"summary_fields": ["summary", "description", "tags"],
"published_at": "published_at"
}
}- No credentials are needed for the included public feeds.
- History stores hashed role keys, not raw job URLs.
- The validator blocks email addresses, mail-to-style links, and likely phone numbers in generated public data.
- If you add private/internal feeds, keep secrets out of this repo and update the workflow to read them from GitHub Actions secrets.
config/opportunity_radar.json— main configuration.scripts/update_opportunities.py— fetch, score, rank, and write data.scripts/validate_opportunities.py— quality and privacy gate._data/opportunities.json— generated public data rendered by Jekyll._data/opportunities_history.json— generated privacy-safe trend history.opportunities.md— public page.
MIT.
