Det här dokumentet är den app-specifika, versionsstyrda tekniska kontexten för det tidigare monorepot / apps/jobb.
Senast verifierad mot repositoryt: 2026-10-02
Vid konflikt för repo-specifik teknik gäller följande ordning:
main i det tidigare monorepot, under apps/jobb.docs/organization/ innehåller delad kontext för det tidigare monorepot. Jobbs app-local current-state ägs av apps/jobb, medan Cloudflare-live-state verifieras hos respektive provider. Historik hör hemma i Git.
Jobb söker jobb automatiskt på StudentConsulting och samlar månadens ansökningar i en .txt med jobb-ID, jobbtyp, stad och omfattning. Arbetsförmedlingen har inget API för aktivitetsrapporten, så Jobb rapporterar inte dit.
Systemets hårda mål är 10 verifierade lämpliga ansökningar per kalendermånad. Det är ett tak i automationsflödet, inte bara en dashboard-mätare.
Viktiga säkerhetsgränser:
STUDENTCONSULTING_AUTOSUBMIT=true. OIDC-flödet returnerar till den verifierade profilentryn https://www.studentconsulting.com/sv/profil/; discovery och verifiering återbesöker samma entry i stället för publika /sv/. Om /signin återanvänder en redan autentiserad Browser Run-session och landar direkt i profilområdet utan synliga login-kontroller accepteras sessionen utan ny credential-read eller formulärsubmit; auth godtas bara på exakt StudentConsulting-origin under /sv/profil/. Jobbmatchningsnavigation identifieras semantiskt via etablerade Matcha jobb-varianter samt explicita former av Jobbmatchning(ar) och Matchning(ar) mot jobb; orelaterade publika etiketter accepteras inte. En semantisk matchningsanchor måste ge exakt en betrodd route under /sv/profil/... eller äldre observerade /sv/min-profil/.... Ett benign root-placeholder i href får kombineras med exakt en betrodd data-href eller data-url. Saknas sådan data-route får exakt en synlig semantisk placeholder-anchor klickas. Om URL:en ligger kvar på profilentryn reskannas navigationen en gång efter en nytillkommen betrodd semantisk route/kontroll, utan rekursivt omklick av samma placeholder. Resultat-routen måste validera till det betrodda profilområdet. Externa, credential-bärande, utanför-profil-redirects eller ambivalenta alternativ failar stängt. Om sådan ankarlänk saknas får en enda entydig synlig button/role-link/data-navigation-kontroll aktiveras, men destinations-URL:en måste efter klick passera samma origin-/profilvalidering. Saknad/otillåten/ambivalent matchningsnavigation stoppar körningen och ingen hårdkodad profilfallback används. Vid saknad träff får diagnostik endast exponera högst 20 same-origin /sv/profil/...- eller äldre /sv/min-profil/...-paths samt strukturräknare för totala/synliga anchors och kontroller, semantiska matchningar och data-href/data-url/onclick; aldrig etiketter, profiltext, queryvärden, attributpayloads eller inputvärden. Synliga login-kontroller kontrolleras efter auth, profilöppning och varje listing-navigation. Okänd vald matched-page-struktur rapporterar endast sanerade elementräknare. Land enrichas från StudentConsultings publika jobb-API med explicita landsfilter för Sverige/Norge/Danmark när detaljsidan saknar Land; olöst eller ambivalent land stoppar kandidaten före autosubmit.is_applied=true; annars används den autentiserade Ansökningar-vyn och exakt Jobb-ID måste finnas som text eller i en betrodd StudentConsulting job-detail-URL. Ansökningar-navigation använder exakt origin/profilområde och failar stängt vid extern, credential-bärande eller ambivalent navigation; reconciliation skickar aldrig om en uncertain ansökan.failed med applied_at IS NULL och verified_at IS NULL; nästa försök får högre attempt_no och application-historiken flyttas inte mellan runs. queued, applying, submitted, verified, needs_user_action eller någon rad som kan ha skickats återköas inte. Ett osäkert submit-resultat behåller reservationen som uncertain; systemet kompenserar inte med en potentiell elfte ansökan. Dashboardens tio synliga månadsplatser är däremot mål-/resultatplatser och fylls endast av verified ansökningar; needs_user_action visas separat som en säkerhetsspärr och räknas inte som godkänd ansökan.Primär runtime är Cloudflare Worker jobb med entrypoint apps/web/src/index.ts.
Appens apps/jobb/wrangler.jsonc är deployment-konfigurationen och binder:
BROWSER.D1_DATABASE_NAME) som DB.jobb-evidence som EVIDENCE.EMAIL.jobb-automation, klass JobAutomationWorkflow, som JOB_AUTOMATION.0 9 10-13 * *.<din-host>.Workspace använder pnpm 10.17.1. Root-skripten kör repoövergripande typecheck/test och produktionens deploykommando applicerar D1-migrationer före Wrangler deploy.
Den skyddade dashboarden kan starta samma pipeline manuellt under den 1:a–14:e varje månad i Europe/Stockholm.
Manuell start kräver:
Starten gör därefter en atomisk D1-claim: högst en running automation får finnas åt gången över manuella och schemalagda starter. Run-raden skapas eller, för en failed schemalagd retry, återställs till ett rent running-state innan Workflow startas. Workflow-ID länkas både direkt efter create och som första Workflow-step. Startfel och Workflow-undantag skrivs tillbaka som failed; gamla running-rader utan Workflow-länk eller ansökningsaktivitet klassas/reconcileras som orphaned efter fem minuter.
Cron kör en gång per dag den 10:e–13:e vid 09:00 UTC.
scheduled:YYYY-MM fortfarande är failed.completed eller running startas inte om.Produktionsbindingen DB ska peka på en D1-databas som skapats med Cloudflare-jurisdiction eu. Jurisdiction är creation-time providerkonfiguration och ska verifieras i Cloudflare när databasen ersätts; repositoryts UUID/name-binding är inte i sig bevis på data locality.
D1-migrationerna är canonical schemahistorik:
0001_initial.sql — jobb, ansökningar, attempts och evidence (samt reports, borttagen i 0009).0002_automation.sql — automation runs och notifieringshistorik.0003_integration_probes.sql — sanitiserade metadata för autentiserade integrationsprobes.0004_monthly_application_quota.sql — exakt tio quota-slots per månad.0005_activity_report_submission.sql — state för AF-rapportaktiviteter (borttagen i 0009).0006_runtime_configuration.sql — krypterad dashboard-hanterad runtimekonfiguration.0007_monthly_activity_exports.sql — månadsfilen (.txt) med dagen då den blev klar.0008_runtime_configuration_access_identity.sql — Access-identitet för konfigurationsändringar.0009_drop_af_reporting.sql — tar bort AF-rapporteringens tabeller och bygger om notifications för e-postutskick av månadsfilen.Centrala stateflöden:
queued → applying → submitted → verified, med failed och needs_user_action som explicita avvikelser.started → submitted|verified|failed|unknown.free → reserved → submitted|verified|uncertain.running → completed|failed; en failed schemalagd retry nollställer tidigare completion/error/workflow-fält innan nytt Workflow startas. Dashboarden visar äldre oanslutna running-rader som orphaned i stället för som verkligt aktiva.Verifierad StudentConsulting-evidens skrivs som JSON till privata R2-bucketen jobb-evidence. D1-tabellen evidence lagrar metadata och R2 object key.
Schemafältet evidence.sha256 finns men fylls inte av nuvarande lagringsväg. Det ska därför inte beskrivas som ett aktivt integritetsskydd förrän hashing faktiskt implementerats och verifierats.
Dashboarden visar evidence-metadata men är inte en generell R2 object-browser.
StudentConsulting-integrationen använder Browser Run för autentisering, discovery, formulärkontroll, submission och verifiering. Discovery läser navigationsvärden från href, data-href, data-url och inline onclick på den verifierade Matcha-jobb-vyn. Absoluta URL:er behåller sin ursprungliga authority vid validering; endast betrodda StudentConsulting-rutter av formen /sv/lediga-jobb/.../<Jobb-ID> får bli kandidater.
Autonom submission kräver:
STUDENTCONSULTING_AUTOSUBMIT=true,Ett definitivt misslyckande kan frigöra en reserverad slot. Ett osäkert resultat behåller sloten för att förhindra dubbel/elfte ansökan.
När månaden har tio verifierade ansökningar sparas en .txt i D1 (activity_report_exports). Varje jobb får fyra rader: Jobb-ID, Jobbtyp (specifikt yrke, eller yrkesordet i rubriken när StudentConsulting bara anger en kategori som “Logistik / Transport”), Stad och Omfattning (från annonsen, annars heltid/deltid/extrajobb i rubriken). Filen heter jobbansokningar_ÅÅÅÅ-MM-DD.txt efter dagen då den tionde ansökan verifierades. Filer i det äldre formatet byggs om en gång vid första nedladdning eller utskick.
En ny fil mejlas automatiskt till NOTIFY_EMAIL_TO (en eller flera adresser). NOTIFY_EMAIL_MODE=group skickar ett gemensamt mail, separate ett mail per mottagare. Filen bifogas och står även i brödtexten. Varje utskick loggas i notifications; ett misslyckat utskick fäller aldrig körningen.
Historiska luckor får repareras read-only från StudentConsultings autentiserade Ansökningar-historik. Endast poster som StudentConsulting själv returnerar för den uttryckligen valda månaden får backfillas; reparationsflödet får aldrig skicka eller bakdatera en ny ansökan.
Dashboarden på / är operativt kontrollplan. Den skyddas av Cloudflare Access och kräver komplett konfiguration för CF_ACCESS_TEAM_DOMAIN och CF_ACCESS_AUD; ofullständig konfiguration failar stängt.
Dashboarden erbjuder legacy, forest (med etiketten från SITE_FAMILY) och blackout med Legacy som fallback. Legacy bygger på Portalens faktiska pre-v2 portalen-uttryck med cyan/blå/magenta glow och diskret rutnät; de cyan/gröna operativa accenterna förblir Jobb-specifika. Dashboarden persisterar samma presentationspreferens som övriga monorepoappar; cookien är aldrig auth-, CSRF- eller sessionsstate.
Top-level-vyer:
needs_user_action kan kontrolleras mot StudentConsulting igen eller uttryckligen markeras som ej inskickad.Primära endpoints:
GET /login — leder till / (inloggning sker i Cloudflare Access framför Workern).POST /auth/logout — skickar vidare till /cdn-cgi/access/logout; kräver autentiserad same-origin request.GET /api/health — minimal liveness.GET /api/ready — minimal readiness; läser SELECT 1 från D1 och verifierar att dashboard-auth är faktiskt användbar. Access-konfigurationen kontrolleras och Secrets Store-bindingen JOBB_CONFIG_KEY läses, men inga credential-värden returneras och inga externa provideranrop görs.GET /api/dashboard — canonical dashboard read model utan hemliga credential-värden.POST /api/configuration — same-origin, autentiserad write av dashboard-hanterad krypterad runtimekonfiguration.POST /api/applications/:id/recheck — same-origin, autentiserad provider-omkontroll av en needs_user_action-ansökan; kan bara verifiera när StudentConsulting bekräftar exakt Jobb-ID.POST /api/applications/:id/not-submitted — same-origin, autentiserad explicit användarbekräftelse att en osäker ansökan inte skickades; frisläpper den interna säkerhetsreservationen men ger aldrig verifieringscredit.GET /api/runs/:id — read-only run-detail.POST /api/runs/manual — manuell Workflow-start.POST /api/runs/:id/stop — terminerar en aktiv Workflow-instans och finaliserar runnen som manuellt stoppad.DELETE /api/runs/:id — raderar terminal run utan ansökningshistorik samt run-bundna probes/notifications och Workflow-state.GET /api/reports/:month/export.txt — månadsfilen som jobbansokningar_ÅÅÅÅ-MM-DD.txt.POST /api/reports/:month/email — same-origin, autentiserat utskick av månadsfilen till de konfigurerade mottagarna.Alla skyddade mutationer kräver exakt same-origin Origin; inkompatibel Sec-Fetch-Site avvisas också när headern finns. Detta är ett extra CSRF-skydd ovanpå dashboard-auth. Manuell run, stop och delete kräver samma Access-auth och same-origin-skydd.
Dashboard-CSP tillåter endast egna scripts/styles/connect-källor; unsafe-inline används inte och ingen extern Turnstile-scriptkälla behövs.
Browser-felsidor använder separat same-origin CSS på /assets/auth.css, strikt CSP utan inline-script och en gemensam felvy med sanitiserad felkod och korrelations-ID.
Hemliga värden får aldrig committas. Produktionsvärden kan komma från Cloudflare/runtime eller, för de fält som dashboarden hanterar, från den krypterade D1-raden runtime_configuration. Deployment-värden har alltid företräde framför dashboard-värden.
Credential-/security-namn:
JOBB_CONFIG_KEY — nyckel för den krypterade runtime-konfigurationen; produktion läser värdet via Cloudflare Secrets Store-binding medan lokal utveckling kan använda en vanlig runtime-strängJOBB_CONFIG_KEY_PREVIOUS — endast under nyckelbyte; föregående nyckel så att befintlig ciphertext kan krypteras omSTUDENTCONSULTING_EMAILSTUDENTCONSULTING_PASSWORDIcke-hemliga eller policyrelaterade runtime-värden:
CF_ACCESS_TEAM_DOMAIN — Cloudflare Access team domain (<team>.cloudflareaccess.com)CF_ACCESS_AUD — Application Audience-tagg för Access-appen för <din-host>STUDENTCONSULTING_AUTOSUBMITJOB_INCLUDE_TERMS — valfritt extra positivt filter ovanpå Matcha jobbJOB_EXCLUDE_TERMSJOB_ALLOWED_LOCATIONSJOB_ALLOWED_COUNTRIESNOTIFY_EMAIL_TO — en eller flera mottagare av månadsfilenNOTIFY_EMAIL_FROMNOTIFY_EMAIL_MODE — group eller separatePUBLIC_BASE_URLUtskick använder Email-bindingen EMAIL.
Den autentiserade System-vyn kan spara StudentConsulting-konto, autosubmit, valfria extra lämplighetsfilter och e-postutskick av månadsfilen. Manuell körning skyddas av Cloudflare Access och exact same-origin mutation guard; Jobb bäddar inte in eller kräver Turnstile. Det dashboard-hanterade dokumentet krypteras med AES-GCM innan D1-write; krypteringsnyckeln härleds med separat HKDF-context från JOBB_CONFIG_KEY. StudentConsulting-lösenordet returneras aldrig efter sparning. Vid nyckelbyte binds den gamla nyckeln som JOBB_CONFIG_KEY_PREVIOUS; första läsningen krypterar om raden med den nya nyckeln, så inget behöver matas in igen.
Cloudflare Access är Jobbs identity authority. Access sköter inloggning och policy framför <din-host>; Workern verifierar på varje request att Cf-Access-Jwt-Assertion är en RS256-signerad JWT från teamets certs med rätt issuer, audience (CF_ACCESS_AUD) och giltighetstid. Jobb har ingen egen session, cookie eller login-provider.
Access är fail-closed och enda dashboard-authvägen: saknad eller halvkonfigurerad CF_ACCESS_TEAM_DOMAIN/CF_ACCESS_AUD ger fel i auth/readiness, och en request utan giltig assertion avvisas även om Access skulle ha förbigåtts. Legacy Basic Auth accepteras inte av koden.
Manuell körning kräver en verifierad Cloudflare Access-identitet och exact same-origin mutation guard. Körningsdetaljer erbjuder stop för aktiva Workflow-instanser och delete för terminala testkörningar utan ansökningshistorik; körningar med ansökningshistorik kan inte raderas via dashboarden.
Dashboardens mutationsendpoints har same-origin-kontroll. UI-responsen sätter CSP, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, frame-ancestors 'none' och Cache-Control: no-store.
Wrangler sätter observability.redact_query_string=true. Det är ett krav eftersom OAuth-callbacken bär kortlivade code/state i query-strängen och de inte ska persisteras i Worker-logs/traces.
Appens versionerade verifieringsväg är:
pnpm install --frozen-lockfile
pnpm typecheck
pnpm test
apps/jobb/package.json definierar produktionskommandot:
pnpm deploy:cloudflare
Kommandot applicerar remote D1-migrationer och deployar därefter Workern med apps/jobb/wrangler.jsonc.
Vilket externt CI/CD-system som eventuellt kör kommandona är inte app-local current-state och dokumenteras inte här.
Uppdatera detta dokument när någon av följande ändras:
Materiella ändringar i security boundary, permissions eller deploymentarkitektur ska förankras innan implementation. Extern governance/live-state verifieras i sitt auktoritativa system och ska inte dupliceras som permanent repo-current-state.