Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Jev Moderation Laravel Package

phoenix1331/laravel-jev-moderation

A Laravel moderation package that lets you flag, review, and approve or reject user-generated content with configurable rules, queues, and status tracking. Integrates cleanly with Eloquent models so you can add moderation workflows to posts, comments, and more with minimal setup.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package slots into the Eloquent model lifecycle rather than requiring a service layer rewrite — the Moderatable trait plus #[Moderate(fields: [...], policy: ...)] attribute means moderation is declared per-model, and the job is dispatched after the saving transaction commits, so it composes with existing request, job, Livewire and seeder write paths. Decision state lives in a dedicated append-only moderation_decisions table with a polymorphic relation, which keeps the audit trail separate from business tables and lets the optional moderation_status column serve as a denormalised read path for visibility scopes. For a TPM this is the right shape: it is additive, per-model opt-in, and reversible by removing the trait. The ModerationDriver contract with JevModeration::extend() is the architecturally important seam — it decouples the workflow (decisions, statuses, scopes, events) from the vendor (Jev/TypeSafe), so the durable asset is the decision ledger, not the classifier.

Integration feasibility. Low-to-moderate effort for a Laravel team. Installation is a single artisan command that publishes config and migration, prompts for mode/policy/migration, and prints env keys; --no-interaction defaults to not migrating, which is the safe CI/CD behaviour. A jev-moderation:ping command validates credentials, lists usable models and performs one sample classification before any traffic flows — this is unusually good pre-flight tooling and materially de-risks the first deploy. Pre/post moderation, fail-open/fail-closed, queue/connection/sync and store-content are all env-driven, so environment promotion does not require code changes. The main integration cost is not the package, it is the surrounding work: a moderator console, notification plumbing for the events, and a policy-tuning cycle against real content.

Technical risk. Several items a TPM must price in:

  • Pre-1.0 API surface. v0.1.0 under semver permits breaking changes in any minor bump. Pin exact versions and avoid letting the package's types (enums, Verdict, Classification) leak across your domain boundaries.
  • Single-vendor dependency. Every check calls TypeSafe's POST /v1/systemone. Outages, price changes, model deprecations (JEV_MODEL aliasing is explicitly warned about) and rate limits (429/529) are outside your control. The driver contract is the mitigation; budget for an adapter to a second provider or a local model as insurance.
  • Unbounded cost. Checks are billed per call and fire on create and on moderated-field change. rescan asks before an interactive run precisely because a backfill is a spend event. Nobody owns this budget yet.
  • Data egress. Content leaves your perimeter to a third party. store_content defaults off (sha256 hash only), which is a good default for the ledger, but the request payload still contains the raw text. This needs a DPA and a residency review.
  • Accuracy is asserted, not proven. The accuracy suite is described as "light" and pinned to preset wording. It is a regression guard, not a guarantee for your domain.
  • Append-only vs erasure. DecisionsAreAppendOnly is excellent for audit integrity and awkward for GDPR/CCPA deletion requests. Hash-only storage is the intended escape hatch; confirm that position with legal.

Key questions. Which Laravel and PHP versions are supported (enums imply PHP 8.1+)? Is the package multi-tenant aware — does the global visibility scope respect tenant boundaries? What is the documented per-check latency and token ceiling (default max_context_tokens 24000, context trimmed first, which silently truncates long documents before classification)? What is the vendor's retention and training policy on submitted content? Who owns the moderation policy config, engineering or trust & safety? Is there a migration path off the package — can decisions be exported and the trait removed without orphaning the ledger?

Integration Approach

Stack fit. Native Laravel: service provider with auto-registered JevModeration facade alias, config publish, queued job, Eloquent global scope, model events, and serialisable events (ContentPending, ContentPassed, ContentFlagged, ContentBlocked, ModerationFailed, plus ContentReported and ReviewResolved) suitable for queued listeners. It assumes a queue worker is running and that a user relation exists for moderationAuthor() unless overridden. Database portability across SQLite, PostgreSQL, MySQL and MariaDB means dev/CI/prod parity with no divergence in the decision schema.

Migration path. (1) Install and publish config with migration deferred. (2) Run jev-moderation:ping in a non-production environment against a scoped key to confirm credentials, model access and a sane classification response. (3) Add the trait and attribute to one low-risk model only. (4) Enable JEV_MODERATION_SYNC=true for a short observation window so classifications run inline with no retries and failures are obvious. (5) Switch to the queue. (6) Backfill existing rows with rescan --dry-run --since, review the projected volume and cost, then run for real in chunks. (7) Only after volume is proven, run jev-moderation:add-column {table} to add the indexed moderation_status column and backfill it from each row's latest decision — this converts visibility queries from joins to index lookups. Do not skip step 7 at scale, and do not run it before step 6 or you will backfill stale statuses.

Compatibility. Confirm the moderation_status column name does not collide with existing schema, and that any existing status/published scopes on the same models compose correctly with withoutModeration(), onlyFlagged(), onlyBlocked() and whereModerationStatus() — stacked global scopes are a classic source of "row disappeared" incidents. The FakeDriver with classifyAs(), failWith() and push() assertions makes the package testable without network calls; require that every team adopting it writes tests against the fake rather than the live driver. Existing rows and any model without the trait are unaffected (models never checked stay visible), which keeps the blast radius small.

Sequencing. Phase 0 (1–2 weeks): spike, ping, cost model, legal/privacy review. Phase 1 (2–4 weeks): one model, post-moderation, fail-open, sync mode, events wired to logging and alerting. Phase 2: moderator console, queue enabled, add-column before traffic grows, jev-moderation:stats reviewed weekly to tune review_above/block_above. Phase 3: broaden to remaining models, evaluate pre-moderation for user-generated public content, and only then consider fail-closed. Phase 4: second-driver adapter as vendor insurance.

Operational Impact

Maintenance. The heavy maintenance load is not code, it is policy. Thresholds, category lists, presets and prompt wording need a named owner and a review cadence; the policy version sha256 gives you traceability, so every decision records which policy produced it — use it in post-incident reviews and in any regulator or trust-and-safety conversation. Track upstream releases closely given pre-1.0 semver, and read each changelog for threshold or default changes that would silently alter behaviour.

Support. Package-level support is community/maintainer best-effort at v0.1.0; the vendor relationship (TypeSafe) is a separate commercial dependency that needs an owner, an SLA expectation and an escalation path. Internally, two roles are needed: an engineer who owns the driver/config/queue plumbing, and a trust-and-safety or community-operations owner who owns the policy and the review queue. Neither can substitute for the other.

Scaling. Throughput is bounded by provider latency and rate limits, not by your database. The queue with configurable connection, tries and backoff absorbs bursts; sync mode does not and should never be used under load. Backoff honours both exponential retry and Jev's retry-after, so 429s degrade gracefully rather than hammering. Rescan is chunked and can be scoped by --status/--since, so large backfills are controllable. The moderation_status column plus index is the main scaling lever on the read path — without it, every listing query joins the append-only decision table, which grows monotonically and never shrinks.

Failure modes. Enumerate and decide each deliberately: vendor timeout (retry, then failed decision); persistent rate limiting (queue drains slowly, content stuck pending); failed check under fail-open (content publishes unreviewed — correct for low-risk, unacceptable for regulated content); fail-closed (content invisible until manual intervention — needs an on-call runbook); worker down (ContentPending fires but nothing resolves — alert on pending age, not pending count); truncation of long content past max_context_tokens (content classified on a prefix only — worst case, a long document's harmful tail is never seen); moderator error (mitigated by append-only: a bad approve cannot be erased, only superseded — so a fresh decision, not an edit, is the recovery path, and your console must present it that way). Decide the mode and fail behaviour per model, not globally, and document the reasoning.

Ramp-up. A Laravel developer can integrate a single model in under a day using the fake driver; full fluency — driver overrides, scopes, policy tuning, event-driven console work — is roughly one to two weeks. The steeper ramp is on the operations side: whoever tunes the policy needs to read jev-moderation:stats (decisions per status, flags and blocks per category, and moderator uphold rate) and understand that a high overturn rate means the threshold is wrong, not the moderators. Budget a calibration sprint after the first real traffic week, and re-run composer test:accuracy plus your own domain-specific eval set before each policy change.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource