Auditlog
Het auditlog legt elke beheeractie in je organisatie vast: wie wat deed, wanneer, vanaf welk IP-adres en op welke resource. De bewaring kent twee lagen: een doorzoekbare actieve laag met de volledige payload van de gebeurtenis (actor, actie, fields_json) gedurende 90 / 180 / 365 / 730 dagen, standaard 90 (standaardabonnement) of 365 (enterprise-abonnementen) en per organisatie instelbaar onder Authenticatiebeleid → Bewaartermijn auditlog; en een cryptografisch ketenarchief van 3 jaar in OBS WORM van T Cloud Public, dat de ankerobjecten van de hashketen bewaart nadat de payload in de actieve laag is geredigeerd. Compliance-exports beslaan het doorzoekbare venster.
Voor de cryptografische bewaarketen (ankerketen, merkle-commitments in OBS) zie Integriteit van de auditketen.
Wat er wordt vastgelegd
| Soort gebeurtenis | Voorbeelden |
|---|---|
| Identiteit | Aanmelding, SSO-step-up, MFA-reset, SCIM-provisioning, rolwijziging |
| Repository | Repository aangemaakt, overgedragen, gearchiveerd, verwijderd; branchbescherming gewijzigd |
| Geheimen | Secret aangemaakt, geroteerd, verwijderd (alleen de naam, nooit de waarde) |
| Webhooks | Bestemming toegevoegd, geroteerd, verwijderd; aflevering opnieuw verstuurd |
| Beleid | Regel voor Push-bescherming toegevoegd of overschrijving toegepast; overschrijving van de malwarescan; negering van een SAST-bevinding |
| Facturatie | Wijziging van abonnement, betaalmethode bijgewerkt, wijziging van het plafond |
| Data-export | Export in de wachtrij gezet, gedownload, verlopen |
Wat er opvallend niet in staat: gewone leesacties (clone, bekijken, een bestand openen). Het auditlog is een log van beheeracties, geen toegangslog. De uitzondering zijn alleen-lezen auditors: wat zij van je instellingen lezen, wordt wel vastgelegd (zie Activiteit van auditors). Toegang tot het pakket- en containerregister (elke download, upload en verwijdering, met actor en IP-adres van de client) staat in het aparte toegangslog van de registry, dat je via de API kunt lezen, naar je SIEM kunt doorsturen en in de data-export meekrijgt. Git-clones en -fetches worden nog niet per verzoek gelogd.
Gebeurtenissen voor pakketten en releases
Het publiceren, taggen, verwijderen en weigeren van interne artefacten staat in het auditlog, in de hashketen zoals elke andere gebeurtenis en in je SIEM-stream. Twee weergaven schrijven ze:
- De versieweergave, vanuit de registry zelf: een versie is verschenen of verdwenen.
- De verzoekweergave, vanuit de uploadproxy die voor elke schrijfactie naar de registry staat: één gebeurtenis per geaccepteerde of geweigerde schrijfactie, met de job achter een jobtoken en, voor tags, waar de tag eerder naar wees.
| Actie | Wanneer |
|---|---|
package.published | Er is een pakketversie verschenen (het eerste bestand is binnengekomen). |
package.deleted | Een pakketversie en haar bestanden zijn weg. |
package.upload.accepted | Een schrijfactie naar de registry is gelukt: een bestand, een npm-publish, een extra bestand in een bestaande versie, een containermanifest dat op digest is gepusht. |
package.upload.refused | fremforge heeft een schrijfactie geweigerd: malware, de scanner niet beschikbaar, te groot om te scannen, een gate voor containerlagen. |
package.delete.accepted | Een verwijdering is gelukt (een bestand, een versie, een containermanifest op digest). |
package.delete.refused | Een verwijdering is geweigerd (anders dan die van een registryrobot, zie hieronder). |
package.tag.created | Een containertag of npm-dist-tag die nog niet bestond, wijst nu naar een digest. |
package.tag.moved | Een containertag wijst nu naar een andere digest. previous_digest is de digest waar hij eerder naar wees. |
package.tag.set | Er is een tag geschreven en fremforge kon niet bepalen waar hij eerder naar wees, of hij wees naar dezelfde digest (unchanged: true). npm-dist-tags zijn altijd set. |
package.tag.deleted | Een tag is verwijderd; digest is waar hij naar wees. |
package.immutable.refused | Het overschrijven van een gepubliceerde versie is geweigerd (onveranderlijke releases); beide sha256-waarden. |
registry_robot.delete_refused | Een registryrobot probeerde iets te verwijderen; robots publiceren, maar verwijderen nooit. |
package.files.recorded, package.files.removed | De bestanden van een versie met hun sha256 (organisaties met pakketbeleid). |
forgejo.release.*, forgejo.ref.create, forgejo.ref.delete | Een release gepubliceerd, bewerkt of verwijderd (met id, doel, draft- en pre-release-vlaggen en assets); een git-tag of -branch aangemaakt (met de commit) of verwijderd. |
Elke pakketgebeurtenis bevat:
| Veld | Betekenis |
|---|---|
org, package_type, package_name, package_version, file | Wat er is geschreven. Een verzoek noemt wat het pad noemt; de versieweergave noemt de versie. |
digest | sha256:<hex>: de digest van een containermanifest, of de sha256 van de body van een ruwe upload (de sha256 van het bestand zelf voor elk type behalve npm en Cargo, waarvan de uploadbody het bestand omhult). |
actor_kind | human, robot (een registryrobot), job (een jobtoken van Forgejo Actions), bot (een systeembot van fremforge), system of anonymous. |
actor_login, actor_id | Wie de registry heeft geauthenticeerd. |
job_repo, job_run_id, job_run_number, job_id | Bij een jobtoken: de repository en de run (/<owner>/<repo>/actions/runs/<job_run_number>). |
decision, reason | Bij een weigering, of bij een schrijfactie die een gate in observatiemodus heeft doorgelaten. |
access_event_id | De bijbehorende rij in het toegangslog van de registry, waarin ook het IP-adres van de client staat. |
Gebeurtenissen uit de verzoekweergave bestaan sinds 2026-10-08. Promotie tussen repository’s is in fremforge geen registryconcept, dus er is geen promotiegebeurtenis.
Toegang tot pakketten
Toegang per pakket en beschermde namen schrijven:
| Actie | Wanneer |
|---|---|
package_access.settings.updated | De standaard van de organisatie is gewijzigd (default_mode van en naar). |
package_access.package.updated | De toegangsinstelling van een pakket is aangemaakt of gewijzigd (mode, inherit_repo, changed). |
package_access.package.removed | De instelling en toekenningen van een pakket zijn verwijderd (grants_removed). |
package_access.grant.set | Een toekenning aan een team of aan Actions is toegevoegd of gewijzigd (grantee_kind, grantee_name, permission, previous_permission). |
package_access.grant.removed | Een toekenning is verwijderd. |
package_protection.rule.created, .updated, .deleted | Een regel voor een beschermde naam is gewijzigd (package_type, name_pattern, push, delete). |
package_access.denied | Een verzoek is geweigerd door toegang per pakket of door een beschermde naam (access, needed, permission, reason en de actorvelden hierboven). Hoogstens één keer per uur voor dezelfde actor, hetzelfde pakket en dezelfde toegang. |
package_access.would_deny | Hetzelfde, terwijl fremforge de controle in observatiemodus uitvoerde en het verzoek wel bediende. |
Modelscan
Modelscan schrijft:
| Actie | Wanneer |
|---|---|
security_policy.model_scan.updated | Het modelbeleid van de organisatie is gewijzigd (changed, en diff met de from en to van elke instelling). |
security_policy.model_scan.dismissed | Een bevinding is voor één bestand genegeerd, of de negering is verlengd (sha256, verdict, reason, expires_at, replaced). |
security_policy.model_scan.dismissal_revoked | Een negering is ingetrokken (sha256). |
Een pull die het modelbeleid weigert, is een registry.pull.blocked-gebeurtenis met gate: model, zie hieronder.
Gates voor container-pulls
De image-gates van de registry schrijven hun beslissingen naar het auditlog, zodat ze net als elke andere gebeurtenis je SIEM bereiken:
| Actie | Wanneer |
|---|---|
registry.pull.blocked | Een container-pull is geweigerd |
registry.pull.would_block | Er is een pull bediend die een gate zou hebben geweigerd: de blokkeeractie van je imagescan is warn, of de gate draait in observatiemodus |
De actor is system:registry-pull-gate. De fields zijn:
| Veld | Betekenis |
|---|---|
gate | image_scan (kwetsbaarheidsbevindingen op of boven je blokkeerdrempel), image_malware (kwaadaardige pakketten), registry_scan (een laag van het image die ClamAV geïnfecteerd vond of niet kon scannen), registry_blob (hetzelfde, voor een laag die rechtstreeks op digest is opgehaald), signature (image-handtekeningen: reason is unsigned, untrusted of invalid) of model (modelscan: reason is model-malicious, model-suspicious of model-unscannable) |
image, ref, digest | Het image, de tag of digest waarom werd gevraagd, en de digest die is geleverd |
reason | Wat de gate heeft gevonden, bijvoorbeeld layer-infected of image-malware |
mode | enforce, warn (je blokkeeractie) of shadow (observatiemodus) |
block_severity, finding_count | Alleen image_scan: de drempel en het aantal openstaande bevindingen op of boven die drempel |
window_start | Het uur waar deze gebeurtenis voor staat |
previous_window_start, previous_window_count | Het laatste eerdere uur met een gebeurtenis voor dezelfde sleutel, en hoeveel beslissingen dat uur in totaal had; null bij de eerste |
Eén gebeurtenis per uur. Een CI-job die in een lus een geblokkeerd image pullt, zou het log anders vullen. Daarom is er per UTC-uur één gebeurtenis per imagedigest, gate en beslissing, geschreven bij de eerste beslissing van dat uur. Verdere beslissingen in dat uur worden geteld, en dat aantal wordt als previous_window_count gemeld bij de volgende gebeurtenis voor dezelfde sleutel. Het aantal van een uur bereikt je SIEM dus met de volgende gebeurtenis voor die digest, niet aan het eind van het uur; het aantal van het laatste uur met pulls wordt niet verstuurd. Een blocked en een would_block voor dezelfde digest, of twee gates, zijn aparte sleutels.
Een registry.pull.blocked-gebeurtenis activeert ook je alert-webhook voor security.pull.blocked, als je er een hebt ingesteld. Elke pull, toegestaan of niet, staat in het toegangslog van de registry.
Activiteit van auditors
Leden van het team Auditors van je organisatie kunnen de instellingen van je organisatie lezen zonder ze te wijzigen. Wat ze lezen, wordt vastgelegd, zodat je kunt zien wat een auditor heeft bekeken:
| Actie | Wanneer |
|---|---|
tenant.auditor.read | Een auditor heeft een beheerpagina geopend of een leesendpoint van de API aangeroepen. Hoogstens één gebeurtenis per auditor en route per 10 minuten |
tenant.auditor.write_refused | Een auditor heeft een verzoek gestuurd dat iets zou wijzigen, en het is geweigerd. Elke poging |
tenant.auditor.read_refused | Een auditor vroeg om een pagina of endpoint die voor auditors gesloten is, zoals de data-export, en dat is geweigerd. Elke poging |
compliance.evidence.fetched | Iemand, auditor of niet, heeft de ondertekende bundel met compliancebewijs via de API opgehaald. Elke keer |
Bij de drie tenant.auditor.*-gebeurtenissen is de actor de gebruikersnaam van de auditor, en de fields bevatten de route (het routepatroon, zoals /:slug/_admin/members, nooit de querystring), de method, de status bij tenant.auditor.read, via (portal, oauth of token) en, voor een API-token, de token_id.
Het log lezen
Beheerconsole → Rapporten → Auditlog. Filter op actor (<username>, of een agent zoals mcp-client:<client id>; het filter vindt ook de persoon namens wie een agent handelde), actie (secret.created, role.changed, …), periode of betrokken resource. De standaardweergave toont de laatste 7 dagen.
Klik op een rij om de JSON-payload uit te klappen. Gevoelige velden (waarden van secrets, OIDC-tokens, bijlagen met persoonsgegevens) worden al bij het schrijven geredigeerd, dus een uitgeklapte rij toont nooit een waarde die je niet aan de auditor van een klant zou willen laten zien.
CSV-export
Klik op CSV exporteren in een gefilterde weergave om een momentopname te krijgen. De CSV bevat de rijen die op dat moment gefilterd zijn, met een maximum van 50.000 rijen per export. Voor grotere vensters zet je een volledige gegevensexport in de wachtrij via Rapporten → Gegevensexport.
Via de REST API
Het auditlog is beschikbaar via de openbare API. Gebruik dit als je gebeurtenissen naar je eigen analysepipeline streamt, een compliancedashboard bouwt of auditquery’s vanuit een script uitvoert.
- Het log doorzoeken
GET /_app/api/v1/orgs/:slug/audit(filter met?actor_kind=en?since=) - De integriteit van de keten verifiëren
GET /_app/api/v1/orgs/:slug/audit/integrity - Het bewaarvenster wijzigen
PATCH /_app/api/v1/orgs/:slug/auth-policybody:{ "audit_full_payload_retention_days": 365 }
Hiervoor is een Personal Access Token (PAT) met de scope audit:read nodig (of policy:write om het bewaarvenster te wijzigen):
curl -H "Authorization: Bearer ${FREMFORGE_PAT}" \
"https://frem.sh/_app/api/v1/orgs/acme/audit?actor_kind=human&since=2026-01-01T00:00:00Z"Zie de referentie van de openbare REST API voor de volledige OpenAPI-specificatie.
Doorsturen naar SIEM
Elke auditgebeurtenis wordt ook naar een HTTPS-ontvanger per organisatie gestuurd als je Doorsturen naar SIEM instelt. De ontvanger krijgt de gebeurtenissen in realtime (binnen enkele seconden na de actie) in een compacte JSON-vorm; het formaat is stabiel en geversioneerd.
Bewaring
Auditrecords worden in twee lagen bewaard:
- Doorzoekbare payloadlaag: de naam van de actor en de
fields_json-payload van elke gebeurtenis zijn gedurende het bewaarvenster hieronder doorzoekbaar in de beheerconsole, de CSV-export en de SIEM-stream. - Manipulatiebestendige ketenlaag: de cryptografische hash en de ketenschakel van elke gebeurtenis blijven 3 jaar bewaard (WORM-verankerd met OBS Object Lock). Dit maakt aantoonbaar dat de keten niet is gemanipuleerd, wat je voor een audit nodig hebt; deze laag blijft intact, ook nadat de redactie van de payloadlaag een rij heeft bewerkt.
Bewaring van de doorzoekbare payload
| Abonnement | Standaard | Instelbare waarden |
|---|---|---|
| fremforge / starter | 90 dagen | 90 / 180 / 365 / 730 |
| enterprise / enterprise_trial | 365 dagen | 90 / 180 / 365 / 730 |
Het maximum van 730 dagen (≈2 jaar) houdt de live tabel audit_events begrensd. Alles daarna is de taak van de WORM-laag; heb je doorzoekbare geschiedenis van meer dan 730 dagen nodig, zet dan elk kwartaal een gegevensexport in de wachtrij en archiveer die in je SIEM.
De grens geldt voor de kolommen actor en fields_json: rijen die ouder zijn dan het venster houden hun hash, tijdstip en actienaam (zodat rapportage voor SOC 2 en DORA blijft werken), maar de actor wordt gepseudonimiseerd tot redacted en de payload wordt vervangen door {}. De cryptografische keten wordt hierdoor niet verbroken; redactie is een pseudonimisering ter plaatse, geen verwijdering.
Het doorzoekbare venster is de klasse audit_payload van Bewaring van bewijs, waar ook de andere klassen van bewijs worden ingesteld. Een juridische bewaarplicht schort de redactie op.
Het doorzoekbare venster wijzigen
Beheerconsole → Beveiliging → Authenticatiebeleid → kaart Bewaartermijn auditlog. Kies 90 / 180 / 365 / 730 dagen. Let op:
- Verlengen van het venster (bijv. 90 → 365): nieuw geschreven rijen blijven langer doorzoekbaar. Rijen die onder het vorige venster al waren geredigeerd, blijven geredigeerd; fremforge maakt een redactie niet ongedaan.
- Verkorten van het venster (bijv. 365 → 90): de volgende dagelijkse redactieronde redigeert elke rij die ouder is dan het nieuwe venster. Een toegepaste redactie kan niet ongedaan worden gemaakt; de WORM-keten wordt niet geraakt.
Auditactie die bij deze instelling wordt vastgelegd: tenant.auth_policy.audit_retention.updated (met het oude en nieuwe aantal dagen in previous en next).
Compliance-toezeggingen
Bijlage A.7 van de verwerkersovereenkomst (DPA) legt de basis van 90 dagen plus 3 jaar WORM vast. De enterprise-SLA legt 365 dagen doorzoekbaar plus 3 jaar WORM vast. Onder de standaard van je abonnement gaan is in de interface toegestaan, maar zou de DPA schenden; fremforge dwingt die ondergrens niet af in de kolom, het contract doet dat. Een storing van de dagelijkse redactieronde zelf (los van een klant die de bewaring wijzigt) wordt continu bewaakt en gealarmeerd.