marque / cennad
REST API for Marque tracker platform
Requires
- php: ^8.3
- illuminate/auth: ^13.0
- illuminate/http: ^13.0
- illuminate/routing: ^13.0
- illuminate/support: ^13.0
- marque/trove: ^4.5
Requires (Dev)
- laravel/pao: ^1.1
- laravel/sanctum: ^4.0
- marque/threepio: ^3.0|^4.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.7
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
REST API for the Marque tracker platform. Provides JSON endpoints for torrent management.
Installation
Requires marque/trove.
composer require marque/cennad
Publish the config:
php artisan vendor:publish --tag=cennad-config
Endpoints
All endpoints require authentication by default and return JSON. Read endpoints can be opened to guests for a public tracker — see Authentication.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/torrents |
List torrents (paginated, searchable) |
| GET | /api/torrents/{id} |
Get torrent details |
| POST | /api/torrents |
Upload a torrent (Uploader+) |
| PUT | /api/torrents/{id} |
Update torrent (name, description) |
| DELETE | /api/torrents/{id} |
Delete torrent |
List Torrents
GET /api/torrents?search=ubuntu&page=2
Returns paginated results with standard Laravel pagination metadata.
Get Torrent
GET /api/torrents/1
Response:
{
"data": {
"id": 1,
"info_hash": "a1b2c3...",
"name": "Example Torrent",
"description": "Description text",
"size": 734003200,
"size_formatted": "700 MB",
"file_count": 12,
"seeders": 4,
"leechers": 1,
"has_torrent_file": true,
"created_at": "2026-01-15T10:30:00.000000Z",
"updated_at": "2026-01-15T10:30:00.000000Z",
"user": {
"id": 1,
"name": "uploader"
},
"links": {
"self": "https://example.com/api/torrents/1",
"download": "https://example.com/torrents/1/download"
}
}
}
Upload Torrent
POST /api/torrents
Content-Type: multipart/form-data
torrent_file=<file>, name=..., description=...
Returns 201 with the torrent, plus meta.warnings (any warnings from the tracker's rules,
e.g. a public torrent on a tracker that warns about them). The installed tracker's rules are
checked before anything is stored. A refused torrent is a 422 validation error on
torrent_file that says what to change: a public torrent on a private tracker, a v2-only
torrent, or a file that isn't a torrent at all.
Update Torrent
PUT /api/torrents/1
Content-Type: application/json
{
"name": "Updated Name",
"description": "Updated description"
}
Requires ownership or Moderator+ role.
Delete Torrent
DELETE /api/torrents/1
Requires Moderator+ role. Returns 204 No Content.
Authorization
Cennad uses Trove's TorrentPolicy for access control:
| Action | Who Can |
|---|---|
| List | Anyone read_middleware lets through. The list only holds torrents the viewer's role may see |
| View | The same, except a torrent with a min_role needs a viewer at that role or above (guests see only unrestricted torrents) |
| Update | Torrent owner or Moderator+ |
| Delete | Moderator+ |
Configuration
Published to config/cennad.php:
| Key | Default | Description |
|---|---|---|
prefix |
api |
URL prefix for all endpoints |
read_middleware |
['api', 'auth:api'] |
Middleware for index and show |
write_middleware |
['api', 'auth:api'] |
Middleware for store, update, destroy |
route_names.prefix |
cennad |
Route name prefix |
route_names.download |
torrents.download |
Download route name (for link generation) |
rate_limit |
60 |
Requests per minute, per user (per IP for guests). 0 or null turns it off |
public_middleware and protected_middleware are the pre-4.0 names for read_middleware
and write_middleware. They still work and still take precedence, but they emit a
deprecation notice and are removed in 5.0.
Rate limiting
Every cennad route carries throttle:cennad, a named limiter allowing rate_limit
requests a minute (60 by default). Reads and writes share one count. Signed-in users are
counted by their id, and everyone else by IP. On read routes opened to guests no guard
runs, so a client sending a token there is counted by IP too. Past the limit a request
gets 429 Too Many Requests with a Retry-After header. While the limit is on, every
response carries X-RateLimit-Limit and X-RateLimit-Remaining.
The limiter is appended after your read_middleware and write_middleware, so replacing
those lists can't drop it by accident. To turn it off, for example because your app
already throttles its API, set CENNAD_RATE_LIMIT=0. For a different shape (per token,
or one limit for reads and another for writes), redefine the cennad limiter in your own
service provider after cennad's has booted:
RateLimiter::for('cennad', fn (Request $request) => Limit::perMinute(300)->by($request->user()?->id ?: $request->ip()));
Authentication
The default middleware uses auth:api, which needs a guard named api in
config/auth.php. Stock Laravel defines only web, and neither passport:install nor
install:api adds one, so you add it yourself. With Passport, that's
'api' => ['driver' => 'passport', 'provider' => 'users']. With Sanctum, either define 'api' => ['driver' => 'sanctum', 'provider' => null],
or set read_middleware and write_middleware to ['api', 'auth:sanctum']. Any guard
works, as long as the middleware names it.
Bans reach the API through the api group. With usarrs 8.3 or later installed, a
banned, disabled or pending member's token gets a 403 on every cennad route, because
usarrs runs its check in that group. Keep 'api' in both middleware lists if you replace them, or add
usarrs' EnsureUserIsActive yourself.
Reads and writes are configured separately so a public tracker can expose its catalogue without exposing its write endpoints. Both default to requiring authentication, because Cennad cannot tell whether it is serving a private tracker (bloodhound/guise) or a public one (hound/disguise), and the safe assumption is the private one.
To open the catalogue to unauthenticated visitors, drop the guard from read_middleware:
'read_middleware' => ['api'],
Leave write_middleware alone when you do — uploads, edits, and deletes are still
governed by Trove's TorrentPolicy on top of whatever middleware you set.
Requirements
- PHP 8.3+
- Laravel 13+
- marque/trove
License
MIT. See LICENSE.