About GMAT

GMAT indexes music metadata from IPFS. Each archive is an album directory containing a JSON manifest and the audio files it describes. Search returns albums, prioritizing album metadata such as titles, artists, credits, genres, and tags. Song metadata is also searchable, with support for partial words and small typos.

Archive preparation guide · Public API reference

1. Prepare an album directory

An “archive” is an IPFS directory, not a ZIP or TAR file. Put manifest.json directly at its root. Include the referenced songs and, optionally, a cover image.

example-album/
├── manifest.json
├── art/
│   └── cover.png
└── tracks/
    ├── 01-first-song.wav
    └── 02-second-song.wav

Use paths relative to the album root, with exact spelling and case, such as tracks/01-first-song.wav. Do not use local absolute paths or gateway URLs for file or cover.

2. Write manifest.json

The manifest must be valid JSON, no larger than 1 MiB (1,048,576 bytes). Here is a complete example for the directory above:

{
  "title": "Example Album",
  "primary_artist": "Example Artist",
  "explicit": false,
  "album_type": "album",
  "release_date": "2026-10-01",
  "genres": ["Electronic"],
  "tags": ["independent"],
  "cover": "art/cover.png",
  "songs": [
    {
      "title": "First Song",
      "file": "tracks/01-first-song.wav",
      "track": 1,
      "explicit": false,
      "primary_artist": "Example Artist",
      "duration_seconds": 180
    },
    {
      "title": "Second Song",
      "file": "tracks/02-second-song.wav",
      "track": 2,
      "explicit": false,
      "primary_artist": "Example Artist",
      "duration_seconds": 210
    }
  ]
}

Required fields

Use JSON booleans true and false, and numbers without quotes. Repeat the song’s primary artist even when it is the same as the album artist.

Optional album fields

Optional song fields

Unknown fields are rejected at both album and song level. Omit optional fields you do not need; do not use null in their place.

3. Publish the directory to IPFS

With a running Kubo node, add the complete directory:

ipfs add -r example-album

Copy the CID on the output line for example-album itself. Submit that root directory CID, not the CID of manifest.json, an individual song, or the cover.

Confirm that the manifest is reachable beneath the root:

ipfs cat /ipfs/ROOT_CID/manifest.json

Replace ROOT_CID with your directory CID. Keep the directory pinned and available from your IPFS node or a pinning service. GMAT does not pin submissions for you. Changing any file creates a new directory CID.

4. Submit and validate

On the CID submission page, paste the root CID and submit it. GMAT checks that it is new, retrieves the manifest, validates the schema, and inspects the first 512 bytes of every song before indexing.

Audio must be recognized by the server’s content detector and audio allowlist. A filename extension alone is insufficient, and not every audio format is recognized. The WAV files in this example should contain real WAV audio.

Content detection recognizes native FLAC, MP3, WAV, Ogg audio (including Vorbis and Opus), AAC, audio-branded MP4/M4A, and AMR. It identifies formats from their headers rather than decoding the entire audio file. Generic containers whose audio type cannot be identified from the prefix may still be rejected.

A cover is optional. If provided, ensure its relative path resolves to a real image file. Cover files are fetched when displayed, rather than validated during submission.

If submission fails, check the root CID, manifest filename, JSON types and field names, 1 MiB size limit, song paths, audio content, and IPFS availability. An already-indexed CID cannot be submitted again.

Public API reference

The /api/ endpoints are intentionally public and can be called directly by other applications. No authentication or API key is required. The public base URL is https://gmat.gripe.

These endpoints return JSON, plain text, or file bytes rather than the HTML pages used by the website. Errors are plain text with a non-success HTTP status. Clients should check the status before decoding a response. Responses can be gzip-compressed when requested with Accept-Encoding: gzip; curl --compressed handles decompression.

Cross-origin browser access is not enabled with CORS headers. Same-origin browser clients and server-side or command-line clients can use the API. API endpoints accept only the methods listed below; other methods, including HEAD, return 405.

Search indexed albums using URL query parameters. Each archive appears once, with its complete track list:

curl --compressed --get 'https://gmat.gripe/api/search' \
  --data-urlencode 'q=Deftones' \
  --data-urlencode 'limit=10'

200 OK returns application/json: an array of albums, or [] when no albums match. Each result includes the album fields described above, plus cid (album directory root CID) and songs (the complete track list, ordered by disc and track). Optional metadata may be omitted. An album with no tracks has songs: [].

[
  {
    "title": "Example Album",
    "explicit": false,
    "primary_artist": "Example Artist",
    "cid": "ROOT_CID",
    "cover": "art/cover.png",
    "songs": [
      {
        "title": "Example Song",
        "file": "tracks/song.mp3",
        "track": 1,
        "explicit": false,
        "primary_artist": "Example Artist"
      }
    ]
  }
]

ROOT_CID is a placeholder for a real CID. The file and cover values are paths relative to that root, not complete URLs. Resolve a song through IPFS at /ipfs/{cid}/{file}, URL-encoding path components when using an HTTP gateway. GMAT does not expose a song-streaming endpoint.

Search is case-insensitive and splits text at punctuation. Every query term must match album metadata, or the combined metadata of the album and a single song. Albums matching on their own metadata rank ahead of song-only matches. Within each group, exact matches rank ahead of substring and typo matches; terms of 4–7 characters allow one edit and terms of at least 8 characters allow two. Ties use the archive CID. Multiple matching songs do not create duplicate album results. There is no pagination parameter.

Status codes: 200 results; 400 invalid query or limit; 405 wrong method (with Allow: GET); 500 backend failure.

POST /api/upload

Register an album directory already available on IPFS. Send the root CID as plain text in the request body, such as a bafy… or Qm… CID. Surrounding whitespace, including a final newline, is ignored. Send only one CID, without a URL or path prefix; do not send binary CID bytes, JSON, a form field, a manifest, audio files, or a multipart form.

The request body is limited to 256 bytes, including whitespace. Content-Type: text/plain is recommended, although the handler does not require a particular content type. The directory must follow the archive guide above, including a root manifest.json no larger than 1 MiB and accessible audio files.

curl --fail-with-body --compressed \
  'https://gmat.gripe/api/upload' \
  --header 'Content-Type: text/plain' \
  --data-raw 'ROOT_CID'

Replace ROOT_CID with the album directory CID. To submit a CID saved in a text file, use --data-binary @cid.txt instead of --data-raw 'ROOT_CID'; curl sends the file's text unchanged.

The server checks for an existing CID, retrieves and validates the manifest, checks the detected audio type of every song, and writes the metadata to the database. Successful submissions are immediately searchable. This endpoint neither transfers your local files to IPFS nor explicitly pins the archive.

Status codes: 200 with plain text CID added!; 400 invalid CID, unreadable request body, missing/non-file/oversized manifest, invalid manifest JSON/schema, unreadable song, or unrecognized audio; 405 wrong method (with Allow: POST); 409 CID already indexed; 413 CID request body exceeds 256 bytes; 500 database or unexpected IPFS failure.

Cover images

Search entries include an “Open cover” link when the manifest has a nonempty cover path. The link opens https://ipfs.io/ipfs/{cid}/{cover} in a new tab. Enable “Show covers” to embed covers in iframes at https://{cid}.ipfs.inbrowser.link/{cover}; this option is off by default. The iframe uses the album directory root CID in CIDv1/base32 form. URL-encode path components. Cover availability is not checked during upload.