# Audit event catalogue retoor Every state-changing action in DevPlace records one append-only row through `devplacepy/services/audit/record.py` (`record` for a request-scoped actor, `record_system` for a background one). This file is the authoritative catalogue of the 288 event keys currently emitted, grouped by the category `services/audit/categories.py` `category_for` resolves them to. ## Rules - An event key is `..` (or `.` when the domain is the noun). The domain is the first segment and MUST have an entry in `CATEGORY_BY_PREFIX`; an unmapped domain resolves to category `other` and is a bug. - `target_type` names the entity acted upon, not the event (`gateway_quota`, not `gateway_quota_reset`). - Every row carries a result: `success`, `failure`, `denied`. A refused action is recorded with `denied`, never dropped. - Actor kinds: `user`, `guest`, `system`, `cli`, `service`. Origins: `web`, `api`, `devii`, `cli`, `service`, `scheduler`, `devrant`. - The recorder never raises into its caller; a failed audit write is logged and swallowed so it can never break the action it describes. - Some families are built from a variable at the call site (`vote.{target_type}.{direction}`, `comment.create.{target_type}`, `quiz.question.{action}`, `profile.customization.{scope}.toggle`, and the `create`/`edit`/`delete` verbs the shared `content.py` helpers emit per content type). Every member of those families is listed below individually. - Adding an event means all three of: the key in this file, a `CATEGORY_BY_PREFIX` entry for a new domain, and the recorder call at the mutation point. ## Categories | Category | Keys | |---|---| | `account` | 10 | | `admin` | 21 | | `ai` | 10 | | `attachment` | 5 | | `auth` | 9 | | `backup` | 2 | | `cli` | 42 | | `container` | 12 | | `content` | 37 | | `database` | 4 | | `devii` | 18 | | `email` | 6 | | `engagement` | 23 | | `game` | 6 | | `ingress` | 1 | | `message` | 2 | | `news` | 9 | | `notification` | 4 | | `project` | 11 | | `project_files` | 14 | | `pubsub` | 1 | | `push` | 2 | | `reward` | 4 | | `security` | 3 | | `service` | 5 | | `social` | 10 | | `telegram` | 5 | | `tools` | 12 | **Total: 288 keys.** ## Account and profile (`account`) | Event key | Recorded in | |---|---| | `profile.ai_correction` | `routers/profile/ai_correction.py` | | `profile.ai_modifier` | `routers/profile/ai_modifier.py` | | `profile.api_key.regenerate` | `routers/profile/index.py` | | `profile.avatar.regenerate` | `routers/profile/avatar.py` | | `profile.customization.global.toggle` | `routers/profile/customization.py` | | `profile.customization.pagetype.toggle` | `routers/profile/customization.py` | | `profile.interactions` | `routers/profile/interactions.py`, `services/devii/actions/dispatcher.py` | | `profile.notification.reset` | `routers/profile/notifications.py` | | `profile.notification.toggle` | `routers/profile/notifications.py` | | `profile.update` | `routers/devrant/auth.py`, `routers/profile/index.py` | ## Administration (`admin`) | Event key | Recorded in | |---|---| | `admin.ai_quota.reset_all` | `routers/admin/aiquota.py` | | `admin.ai_quota.reset_guests` | `routers/admin/aiquota.py` | | `admin.backup.delete` | `routers/admin/backups.py` | | `admin.backup.run` | `routers/admin/backups.py` | | `admin.backup_schedule.create` | `routers/admin/backups.py` | | `admin.backup_schedule.delete` | `routers/admin/backups.py` | | `admin.backup_schedule.toggle` | `routers/admin/backups.py` | | `admin.backup_schedule.update` | `routers/admin/backups.py` | | `admin.devii_task.delete` | `routers/admin/devii_tasks.py` | | `admin.devii_task.disable` | `routers/admin/devii_tasks.py` | | `admin.game.era_end` | `routers/admin/game.py` | | `admin.game.era_start` | `routers/admin/game.py` | | `admin.notification.default` | `routers/admin/notifications.py` | | `admin.setting.update` | `docs_api/groups/admin.py`, `routers/admin/settings.py` | | `admin.trash.purge` | `routers/admin/trash.py` | | `admin.trash.restore` | `routers/admin/trash.py` | | `admin.user.active.disable` | `routers/admin/users.py` | | `admin.user.active.enable` | `routers/admin/users.py` | | `admin.user.ai_quota.reset` | `routers/admin/users.py` | | `admin.user.password.reset` | `routers/admin/users.py` | | `admin.user.role.change` | `routers/admin/users.py` | ## AI gateway (`ai`) | Event key | Recorded in | |---|---| | `ai.clippy.chat` | `routers/devii.py` | | `ai.gateway.call` | `services/openai_gateway/gateway.py`, `services/openai_gateway/usage.py` | | `ai.quota.exceeded` | `routers/devii.py`, `services/openai_gateway/service.py` | | `gateway.model.delete` | `routers/admin/gateway_configs.py` | | `gateway.model.update` | `routers/admin/gateway_configs.py` | | `gateway.provider.delete` | `routers/admin/gateway_configs.py` | | `gateway.provider.update` | `routers/admin/gateway_configs.py` | | `gateway.quota.reset` | `cli/gateway.py`, `routers/admin/gateway_configs.py` | | `gateway.quota_rule.delete` | `cli/gateway.py`, `routers/admin/gateway_configs.py` | | `gateway.quota_rule.update` | `cli/gateway.py`, `routers/admin/gateway_configs.py` | ## Attachments (`attachment`) | Event key | Recorded in | |---|---| | `attachment.delete` | `routers/admin/media.py`, `routers/media.py` | | `attachment.rename` | `routers/uploads.py` | | `attachment.restore` | `routers/media.py` | | `attachment.upload` | `routers/uploads.py` | | `attachment.upload_url` | `routers/uploads.py` | ## Authentication (`auth`) | Event key | Recorded in | |---|---| | `auth.account.disable` | `routers/devrant/auth.py` | | `auth.login.failure` | `routers/auth/login.py`, `routers/devrant/auth.py` | | `auth.login.success` | `routers/auth/login.py`, `routers/devrant/auth.py` | | `auth.logout` | `routers/auth/logout.py` | | `auth.password.forgot_request` | `routers/auth/forgotpassword.py` | | `auth.password.reset_complete` | `routers/auth/resetpassword.py` | | `auth.signup` | `routers/auth/signup.py`, `routers/devrant/auth.py` | | `auth.token.failure` | `routers/auth/token.py` | | `auth.token.issued` | `routers/auth/token.py` | ## Backups (`backup`) | Event key | Recorded in | |---|---| | `job.backup.complete` | `services/backup/service.py` | | `job.backup.failed` | `services/backup/service.py` | ## Command line (`cli`) | Event key | Recorded in | |---|---| | `cli.apikey.backfill` | `cli/apikeys.py` | | `cli.apikey.reset` | `cli/apikeys.py` | | `cli.attachments.prune` | `cli/attachments.py` | | `cli.backups.clear` | `cli/backups.py` | | `cli.backups.prune` | `cli/backups.py` | | `cli.backups.run` | `cli/backups.py` | | `cli.containers.gc_workspaces` | `cli/containers.py` | | `cli.containers.prune` | `cli/containers.py` | | `cli.containers.prune_builds` | `cli/containers.py` | | `cli.containers.reconcile` | `cli/containers.py` | | `cli.deepsearch.clear` | `cli/jobs.py` | | `cli.deepsearch.prune` | `cli/jobs.py` | | `cli.devii.lessons.clear` | `cli/devii.py` | | `cli.devii.lessons.prune` | `cli/devii.py` | | `cli.devii.quota.reset` | `cli/devii.py` | | `cli.devii.task.disable` | `cli/devii.py` | | `cli.devii.task.prune` | `cli/devii.py` | | `cli.emoji.sync` | `cli/migrate.py` | | `cli.forks.clear` | `cli/jobs.py` | | `cli.forks.prune` | `cli/jobs.py` | | `cli.game.era.end` | `cli/game.py` | | `cli.game.era.start` | `cli/game.py` | | `cli.game.market.prune` | `cli/game.py` | | `cli.game.steals.prune` | `cli/game.py` | | `cli.isslop.analyze` | `cli/jobs.py` | | `cli.isslop.clear` | `cli/jobs.py` | | `cli.isslop.prune` | `cli/jobs.py` | | `cli.messaging.prune_tickets` | `cli/messaging.py` | | `cli.news.clear` | `cli/news.py` | | `cli.news.sanitize` | `cli/news.py` | | `cli.quiz.prune` | `cli/quiz.py` | | `cli.role.set` | `cli/roles.py` | | `cli.seo.clear` | `cli/jobs.py` | | `cli.seo.prune` | `cli/jobs.py` | | `cli.seo_meta.clear` | `cli/jobs.py` | | `cli.seo_meta.prune` | `cli/jobs.py` | | `cli.token.issue` | `cli/tokens.py` | | `cli.token.prune` | `cli/tokens.py` | | `cli.token.revoke` | `cli/tokens.py` | | `cli.token.revoke_all` | `cli/tokens.py` | | `cli.zips.clear` | `cli/jobs.py` | | `cli.zips.prune` | `cli/jobs.py` | ## Containers (`container`) | Event key | Recorded in | |---|---| | `container.instance.configure` | `routers/admin/containers.py`, `services/devii/actions/dispatcher.py` | | `container.instance.create` | `routers/admin/containers.py`, `routers/projects/containers/instances.py` | | `container.instance.delete` | `routers/admin/containers.py`, `routers/projects/containers/instances.py` | | `container.instance.exec` | `routers/projects/containers/instances.py`, `services/devii/actions/dispatcher.py` | | `container.instance.shell.close` | `routers/projects/containers/instances.py` | | `container.instance.shell.open` | `routers/projects/containers/instances.py` | | `container.instance.start` | `docs_api/groups/admin.py` | | `container.instance.status` | `services/containers/service.py` | | `container.instance.sync` | `routers/admin/containers.py`, `routers/projects/containers/instances.py` | | `container.reconcile.action` | `services/containers/service.py` | | `container.schedule.create` | `routers/projects/containers/schedules.py`, `services/devii/actions/dispatcher.py` | | `container.schedule.delete` | `routers/projects/containers/schedules.py` | ## Content (`content`) | Event key | Recorded in | |---|---| | `comment.create.gist` | `content.py` | | `comment.create.issue` | `content.py` | | `comment.create.news` | `content.py` | | `comment.create.post` | `content.py` | | `comment.create.project` | `content.py` | | `comment.create.quiz` | `content.py` | | `comment.delete` | `content.py`, `routers/comments.py` | | `comment.edit` | `content.py`, `routers/comments.py` | | `gist.create` | `content.py` | | `gist.delete` | `content.py` | | `gist.edit` | `content.py` | | `issue.attachment.add` | `routers/issues/attachments.py` | | `issue.attachment.delete` | `routers/issues/attachments.py` | | `issue.comment` | `routers/issues/comment.py` | | `issue.create` | `services/jobs/issue_create_service.py` | | `issue.create.request` | `routers/issues/create.py` | | `issue.planning.generate` | `services/jobs/planning_service.py` | | `issue.planning.request` | `routers/issues/planning.py` | | `issue.status` | `routers/issues/status.py` | | `issue.sync.reply` | `services/gitea/service.py` | | `issue.sync.status` | `services/gitea/service.py` | | `post.create` | `content.py` | | `post.delete` | `content.py` | | `post.edit` | `content.py` | | `quiz.attempt.answer` | `routers/quizzes/attempts.py` | | `quiz.attempt.finish` | `routers/quizzes/attempts.py` | | `quiz.attempt.start` | `routers/quizzes/attempts.py` | | `quiz.create` | `content.py` | | `quiz.delete` | `content.py` | | `quiz.edit` | `content.py` | | `quiz.grade.failed` | `routers/quizzes/attempts.py` | | `quiz.import` | `routers/quizzes/index.py` | | `quiz.publish` | `routers/quizzes/index.py` | | `quiz.question.create` | `routers/quizzes/questions.py` | | `quiz.question.delete` | `routers/quizzes/questions.py` | | `quiz.question.edit` | `routers/quizzes/questions.py` | | `quiz.question.reorder` | `routers/quizzes/questions.py` | ## Database API (`database`) | Event key | Recorded in | |---|---| | `database.access.denied` | `routers/dbapi/_shared.py` | | `database.nl.design` | `routers/dbapi/nl.py` | | `database.query` | `routers/dbapi/query.py` | | `database.query.async` | `routers/dbapi/query.py` | ## Devii assistant (`devii`) | Event key | Recorded in | |---|---| | `devii.behavior.update` | `services/devii/actions/dispatcher.py` | | `devii.customization.css.set` | `services/devii/actions/dispatcher.py` | | `devii.customization.js.set` | `services/devii/actions/dispatcher.py` | | `devii.customization.reset` | `services/devii/actions/dispatcher.py` | | `devii.lesson.forget` | `services/devii/actions/dispatcher.py` | | `devii.lesson.reflect` | `services/devii/actions/dispatcher.py` | | `devii.notification.reset` | `services/devii/actions/dispatcher.py` | | `devii.notification.set` | `services/devii/actions/dispatcher.py` | | `devii.task.blocked` | `services/devii/tasks/scheduler.py` | | `devii.task.create` | `services/devii/actions/dispatcher.py` | | `devii.task.deferred` | `services/devii/tasks/scheduler.py` | | `devii.task.delete` | `services/devii/actions/dispatcher.py` | | `devii.task.execute` | `services/devii/tasks/scheduler.py` | | `devii.task.update` | `services/devii/actions/dispatcher.py` | | `devii.tool.create` | `services/devii/actions/dispatcher.py` | | `devii.tool.delete` | `services/devii/actions/dispatcher.py` | | `devii.tool.update` | `services/devii/actions/dispatcher.py` | | `devii.turn` | `services/devii/session/core.py` | ## Email (`email`) | Event key | Recorded in | |---|---| | `email.account.delete` | `services/devii/actions/dispatcher.py` | | `email.account.set` | `services/devii/actions/dispatcher.py` | | `email.message.delete` | `services/devii/actions/dispatcher.py` | | `email.message.flag` | `services/devii/actions/dispatcher.py` | | `email.message.move` | `services/devii/actions/dispatcher.py` | | `email.send` | `services/devii/actions/dispatcher.py` | ## Engagement (`engagement`) | Event key | Recorded in | |---|---| | `bookmark.add` | `content.py`, `routers/bookmarks.py` | | `bookmark.remove` | `content.py`, `routers/bookmarks.py` | | `poll.create` | `routers/posts.py` | | `poll.vote.cast` | `routers/polls.py` | | `poll.vote.change` | `routers/polls.py` | | `poll.vote.clear` | `routers/polls.py` | | `reaction.add` | `routers/reactions.py` | | `reaction.remove` | `routers/reactions.py` | | `vote.comment.clear` | `content.py` | | `vote.comment.down` | `content.py` | | `vote.comment.up` | `content.py` | | `vote.gist.clear` | `content.py` | | `vote.gist.down` | `content.py` | | `vote.gist.up` | `content.py` | | `vote.post.clear` | `content.py` | | `vote.post.down` | `content.py` | | `vote.post.up` | `content.py` | | `vote.project.clear` | `content.py` | | `vote.project.down` | `content.py` | | `vote.project.up` | `content.py` | | `vote.quiz.clear` | `content.py` | | `vote.quiz.down` | `content.py` | | `vote.quiz.up` | `content.py` | ## Code Farm (`game`) | Event key | Recorded in | |---|---| | `game.cosmetic.buy` | `routers/game/index.py` | | `game.defense.downgrade` | `routers/game/index.py` | | `game.defense.upgrade` | `routers/game/index.py` | | `game.grant.claim` | `routers/game/index.py` | | `game.infrastructure.buy` | `routers/game/index.py` | | `game.prestige` | `routers/game/index.py` | ## Container ingress (`ingress`) | Event key | Recorded in | |---|---| | `proxy.access` | `routers/proxy.py` | ## Direct messages (`message`) | Event key | Recorded in | |---|---| | `message.read_on_view` | `routers/messages.py` | | `message.send` | `services/messaging/persist.py` | ## News (`news`) | Event key | Recorded in | |---|---| | `news.delete` | `routers/admin/news.py` | | `news.featured.toggle` | `routers/admin/news.py` | | `news.landing.toggle` | `routers/admin/news.py` | | `news.publish.toggle` | `routers/admin/news.py` | | `news.service.draft` | `services/news/service.py` | | `news.service.ingest` | `services/news/service.py` | | `news.service.landing` | `services/news/service.py` | | `news.service.publish` | `services/news/service.py` | | `news.service.reject` | `services/news/service.py` | ## Notifications (`notification`) | Event key | Recorded in | |---|---| | `notification.create` | `utils/notifications.py` | | `notification.open` | `routers/notifications.py` | | `notification.read.all` | `routers/devrant/notifs.py`, `routers/notifications.py` | | `notification.read.one` | `routers/notifications.py` | ## Projects (`project`) | Event key | Recorded in | |---|---| | `project.create` | `content.py` | | `project.delete` | `content.py` | | `project.edit` | `content.py` | | `project.fork.complete` | `services/jobs/fork_service.py` | | `project.fork.failed` | `services/jobs/fork_service.py` | | `project.fork.request` | `routers/projects/index.py` | | `project.readonly.disable` | `routers/projects/index.py` | | `project.readonly.enable` | `routers/projects/index.py` | | `project.visibility.private` | `routers/projects/index.py` | | `project.visibility.public` | `routers/projects/index.py` | | `project.zip.request` | `routers/projects/index.py` | ## Project files (`project_files`) | Event key | Recorded in | |---|---| | `dir.create` | `routers/projects/files.py` | | `file.append` | `routers/projects/files.py` | | `file.delete` | `routers/projects/files.py` | | `file.delete_lines` | `routers/projects/files.py` | | `file.insert_lines` | `routers/projects/files.py` | | `file.move` | `routers/projects/files.py` | | `file.replace_lines` | `routers/projects/files.py` | | `file.upload` | `routers/projects/files.py` | | `file.write` | `routers/projects/files.py` | | `file.write.create` | `routers/projects/files.py` | | `file.write.overwrite` | `routers/projects/files.py` | | `files.zip.request` | `routers/projects/files.py` | | `job.zip.complete` | `services/jobs/zip_service.py` | | `job.zip.failed` | `services/jobs/zip_service.py` | ## Pub/sub (`pubsub`) | Event key | Recorded in | |---|---| | `pubsub.publish` | `routers/pubsub.py` | ## Web push (`push`) | Event key | Recorded in | |---|---| | `push.subscribe` | `routers/push.py` | | `push.update` | `routers/push.py` | ## Gamification (`reward`) | Event key | Recorded in | |---|---| | `reward.badge.award` | `utils/badges.py` | | `reward.level.up` | `utils/rewards.py` | | `reward.streak.milestone` | `utils/rewards.py` | | `reward.xp.grant` | `utils/rewards.py` | ## Security (`security`) | Event key | Recorded in | |---|---| | `security.authz.denied` | `routers/admin/backups.py`, `services/devii/actions/dispatcher.py` | | `security.maintenance.block` | `main.py` | | `security.rate_limit.block` | `main.py` | ## Background services (`service`) | Event key | Recorded in | |---|---| | `service.config.update` | `routers/admin/services.py` | | `service.logs.clear` | `routers/admin/services.py` | | `service.run_now` | `routers/admin/services.py` | | `service.start` | `routers/admin/services.py` | | `service.stop` | `routers/admin/services.py` | ## Social graph (`social`) | Event key | Recorded in | |---|---| | `award.complete` | `services/jobs/award_service.py` | | `award.failed` | `services/jobs/award_service.py` | | `award.give` | `routers/profile/award.py` | | `award.revoke` | `routers/admin/awards.py` | | `follow.follow` | `routers/follow.py` | | `follow.unfollow` | `routers/follow.py` | | `relation.block` | `routers/relations.py` | | `relation.mute` | `routers/relations.py` | | `relation.unblock` | `routers/relations.py` | | `relation.unmute` | `routers/relations.py` | ## Telegram (`telegram`) | Event key | Recorded in | |---|---| | `telegram.pair.failure` | `services/telegram/bridge.py` | | `telegram.pair.request` | `routers/profile/telegram.py` | | `telegram.pair.success` | `services/telegram/bridge.py` | | `telegram.send` | `services/devii/actions/dispatcher.py` | | `telegram.unpair` | `routers/profile/telegram.py` | ## Developer tools (`tools`) | Event key | Recorded in | |---|---| | `deepsearch.chat` | `routers/tools/deepsearch.py` | | `deepsearch.run.complete` | `services/jobs/deepsearch/service.py` | | `deepsearch.run.failed` | `services/jobs/deepsearch/service.py` | | `deepsearch.run.request` | `routers/tools/deepsearch.py` | | `isslop.run.complete` | `services/jobs/isslop/service.py` | | `isslop.run.failed` | `services/jobs/isslop/service.py` | | `isslop.run.request` | `routers/tools/isslop.py` | | `seo.meta.failed` | `services/jobs/seo_meta_service.py` | | `seo.meta.generate` | `services/jobs/seo_meta_service.py` | | `seo.run.complete` | `services/jobs/seo/service.py` | | `seo.run.failed` | `services/jobs/seo/service.py` | | `seo.run.request` | `routers/tools/seo.py` | ## Mapped domains with no recorder The `backup` prefix has a `CATEGORY_BY_PREFIX` entry but no call site emits a bare `backup.*` key - backup activity is recorded as `admin.backup*` (operator actions) and `job.backup.*` (worker outcomes), both of which resolve to the `backup` category through their own prefixes. Keep the mapping: it is what routes `job.backup.*` correctly.