Mangrove Review Standard Schema Guide

This guide provides a comprehensive reference for implementing reviews that comply with the Mangrove Review Standard (MaReSt). It includes field definitions, examples, and practical guidance for developers.

Introduction

The Mangrove Review Standard uses JSON Web Tokens (JWT) to create cryptographically signed reviews. This guide focuses on the JWT payload structure - the actual review content that gets signed.

Quick Reference

Schema Overview

{
  "sub": "URI",           // REQUIRED: What is being reviewed
  "iat": number,          // REQUIRED: Unix timestamp
  
  // At least ONE of these is required:
  "rating": 0-100,        // Numeric rating
  "opinion": "text",      // Text review (max 1000 chars)
  "action": "type",       // Moderation action
  
  // Optional fields:
  "images": [...],        // Array of image objects
  "metadata": {...}       // Additional context
}

Field Requirements at a Glance

  • ✅ Required: sub, iat
  • ⚠️ At least one: rating, opinion, or action
  • ➕ Optional: images, metadata

Field Definitions

Required Fields

sub (Subject URI)

Type: String
Purpose: Identifies what is being reviewed using a URI

The sub field must use one of these URI schemes:

1. HTTPS - Website Reviews
"sub": "https://example.org"
  • Must be a valid URL
  • Maximum 100 characters
  • Used for reviewing websites, online services, or web pages
2. GEO - Location Reviews
"sub": "geo:40.7128,-74.0060?q=Great+Italian+Restaurant&u=50"
  • Format: geo:latitude,longitude?q=name&u=radius
  • q: URL-encoded place name (required, max 100 chars)
  • u: Uncertainty radius in meters (optional, max 40,000,000)
  • Used for restaurants, hotels, tourist attractions, etc.
3. URN:LEI - Company Reviews
"sub": "urn:lei:969500KSV493XWY0PS33"
  • Must be a valid Legal Entity Identifier (LEI)
  • 20-character alphanumeric code
  • Verified against GLEIF database
4. URN:ISBN - Book Reviews
"sub": "urn:isbn:9781718500440"
  • Must be a valid ISBN-10 or ISBN-13
  • Used for reviewing books
5. URN:MARESI - Review of Reviews
"sub": "urn:maresi:7xQtIlHuDdCVioyztj8i3zJ8dk3oCSfKr6VCR5RtBn6sBcqvpfyvs13PlKGJoamKzx8xUgQTQJjRPv5s91-VLQ"
  • References another review’s signature
  • Used for meta-reviews or moderation actions

iat (Issued At)

Type: Number
Purpose: Timestamp when the review was created

"iat": 1708098842
  • Unix timestamp (seconds since January 1, 1970)
  • Must be a valid timestamp
  • JavaScript: Math.floor(Date.now() / 1000)

Conditional Fields (At Least One Required)

rating

Type: Number
Range: 0-100 (integer) Purpose: Numeric score for the subject

"rating": 85
  • 100 = highest recommendation
  • 0 = lowest recommendation
  • Maps to different scales:
    • 5-star: 1=⭐, 25=⭐⭐, 50=⭐⭐⭐, 75=⭐⭐⭐⭐, 100=⭐⭐⭐⭐⭐
    • Thumbs: 1-50=👎, 51-100=👍
    • Percentage: Direct mapping

opinion

Type: String
Max Length: 1000 characters
Purpose: Text content of the review

"opinion": "Amazing pasta and great service! The ambiance was perfect for a date night."
  • Can contain any UTF-8 text
  • Should be meaningful and relevant to the subject
  • Limited to 1000 characters for consistency

action

Type: String
Values: "delete", "edit", "report_abuse", "equivalence", "identity"
Purpose: Act on an existing review, on a subject, or on the reviewer’s own key

"action": "delete"
  • delete: Remove your own review. sub is the urn:maresi: of the review.
  • edit: Update your own review. sub is the urn:maresi: of the review.
  • report_abuse: Flag another user’s review. sub is the urn:maresi: of the review.
  • equivalence: Claim that two subjects are the same. sub is one subject and opinion the other.
  • identity: Bind the signing key to a decentralized identifier. sub is the DID, and the server accepts the claim only when the DID document lists the key; see the identity guide.

Optional Fields

images

Type: Array of Objects
Max Items: 5 (recommended)
Purpose: Attach images to the review

"images": [
  {
    "src": "https://example.com/images/pasta.jpg",
    "label": "Delicious pasta dish"
  },
  {
    "src": "https://example.com/images/tiramisu.jpg",
    "label": "Homemade tiramisu"
  }
]
  • Each image object requires:
    • src (required): Public URL of the image
    • label (optional): Description (max 50 chars)

metadata

Type: Object
Purpose: Additional context about the reviewer or review

Common metadata fields:

"metadata": {
  "nickname": "FoodLover",
  "experience_context": "couple",
  "is_personal_experience": true,
  "client_id": "https://myapp.com"
}
Identity Fields
  • nickname: Display name (max 50 chars)
  • preferred_username: Account name (max 20 chars)
  • given_name: First name (max 20 chars)
  • family_name: Last name (max 20 chars)
  • birthdate: Date of birth (YYYY-MM-DD format)
  • age: Age in years (max 200)
  • gender: Gender identity (max 20 chars)
Context Fields
  • experience_context: "solo", "couple", "family", "friends", "business"
  • client_id: URI of the app/website used to submit review
Flag Fields (Boolean)
  • is_personal_experience: Direct experience with subject
  • is_affiliated: Reviewer affiliated with subject
  • is_generated: Review created by automated system
Other Fields
  • data_source: URL if data from external source
  • osm_id: OpenStreetMap identifier, optionally with version (e.g., node/10066902935/13, way/12345/4, relation/67890)
  • original_url: URL of original review on external platform (for deduplication when importing)
  • reviewer_index: Unique index for ranking (max 9007199254740991)
  • license: SPDX license identifier ("CC-BY-4.0" or "CC-BY-SA-4.0"). Defaults to "CC-BY-4.0" when not specified.

Complete Examples by Use Case

1. Basic Website Review

{
  "iat": 1708098842,
  "sub": "https://example.org",
  "rating": 85,
  "opinion": "Great testing first link example.",
  "metadata": {
    "nickname": "TestNickName1",
    "client_id": "https://example.org"
  }
}

2. Restaurant Review with Images

{
  "iat": 1708098900,
  "sub": "geo:40.7128,-74.0060?q=Great+Italian+Restaurant&u=50",
  "rating": 90,
  "opinion": "Amazing pasta and great service! The ambiance was perfect for a date night. I would highly recommend their house special and tiramisu.",
  "metadata": {
    "nickname": "FoodLover",
    "experience_context": "couple",
    "is_personal_experience": true
  },
  "images": [
    {
      "src": "https://example.com/images/pasta.jpg",
      "label": "Delicious pasta dish"
    },
    {
      "src": "https://example.com/images/tiramisu.jpg",
      "label": "Homemade tiramisu"
    }
  ]
}

3. Book Review

{
  "iat": 1708098950,
  "sub": "urn:isbn:9781718500440",
  "rating": 85,
  "opinion": "Excellent programming book that helped me understand complex concepts in a straightforward way. The examples are practical and the explanations are clear.",
  "metadata": {
    "nickname": "CodeReader",
    "is_personal_experience": true
  }
}

4. Company Review

{
  "iat": 1708099000,
  "sub": "urn:lei:969500KSV493XWY0PS33",
  "rating": 70,
  "opinion": "Good company to work with. Their customer service was responsive, though there were some delays in delivery.",
  "metadata": {
    "nickname": "BusinessPartner",
    "experience_context": "business",
    "is_personal_experience": true
  }
}

5. Meta-Review (Review of a Review)

{
  "iat": 1708099050,
  "sub": "urn:maresi:7xQtIlHuDdCVioyztj8i3zJ8dk3oCSfKr6VCR5RtBn6sBcqvpfyvs13PlKGJoamKzx8xUgQTQJjRPv5s91-VLQ",
  "rating": 90,
  "opinion": "This review was very helpful and accurately described my experience as well.",
  "metadata": {
    "nickname": "MetaReviewer"
  }
}

6. Delete Action

{
  "iat": 1745329011,
  "sub": "urn:maresi:fWMOYt4feQbeQqpQP1qzOGV3kCExUuj87vIZHPbSV0p7TxuW1xpLfqDFJ3S3WLIVLv_8xP3RsmyuYhbKcFVKYQ",
  "action": "delete",
  "metadata": {
    "client_id": "http://localhost:5173/"
  }
}

7. Edit Action

{
  "iat": 1745348936,
  "sub": "urn:maresi:fWMOYt4feQbeQqpQP1qzOGV3kCExUuj87vIZHPbSV0p7TxuW1xpLfqDFJ3S3WLIVLv_8xP3RsmyuYhbKcFVKYQ",
  "action": "edit",
  "rating": 95,
  "opinion": "Updated opinion with improved rating",
  "metadata": {
    "client_id": "http://localhost:5173/"
  }
}

8. Report Abuse Action

{
  "iat": 1708099200,
  "sub": "urn:maresi:aBC123xyz456def789GHI012jkl345MNO678pqr901STU234vwx567YZA890bcd",
  "action": "report_abuse",
  "opinion": "This review contains promotional content that violates terms of service."
}

9. Comprehensive Review with All Fields

{
  "iat": 1708099250,
  "sub": "geo:48.8584,2.2945?q=Eiffel+Tower+Restaurant&u=100",
  "rating": 95,
  "opinion": "The view from this restaurant is absolutely breathtaking! We visited during sunset and the food was as amazing as the scenery. The staff was incredibly attentive and knowledgeable about the menu. The wine pairing suggestions were perfect.",
  "metadata": {
    "nickname": "TravelFoodie",
    "given_name": "Alex",
    "age": 34,
    "gender": "non-binary",
    "experience_context": "family",
    "is_personal_experience": true,
    "client_id": "https://travel-reviews-app.example.com",
    "preferred_username": "alexfoodie123",
    "birthdate": "1990-05-15"
  },
  "images": [
    {
      "src": "https://example.com/images/view.jpg",
      "label": "View from our table"
    },
    {
      "src": "https://example.com/images/appetizer.jpg",
      "label": "Starter dish"
    },
    {
      "src": "https://example.com/images/main_course.jpg",
      "label": "Main course"
    },
    {
      "src": "https://example.com/images/dessert.jpg",
      "label": "Dessert"
    },
    {
      "src": "https://example.com/images/wine.jpg",
      "label": "Wine selection"
    }
  ]
}

10. Opinion-only Review

{
  "iat": 1708099300,
  "sub": "https://blog.example.com",
  "opinion": "This blog has been a fantastic resource for learning about sustainable practices. The articles are well-researched and practical.",
  "metadata": {
    "nickname": "EcoReader"
  }
}

11. Rating-only Review

{
  "iat": 1708099350,
  "sub": "geo:34.0522,-118.2437?q=Local+Coffee+Shop&u=20",
  "rating": 80,
  "metadata": {
    "nickname": "QuickRater",
    "experience_context": "solo"
  }
}

12. Affiliated Review

{
  "iat": 1708099400,
  "sub": "https://restaurant.example.com",
  "rating": 100,
  "opinion": "As the chef and owner of this restaurant, I'm proud of what we've created. We focus on fresh, local ingredients and creating memorable dining experiences.",
  "metadata": {
    "nickname": "ChefOwner",
    "is_affiliated": true,
    "client_id": "https://restaurant.example.com"
  }
}

13. Imported Review

{
  "iat": 1708099500,
  "sub": "geo:52.5200,13.4050?q=Berlin+Restaurant&u=30",
  "rating": 80,
  "opinion": "Excellent food and friendly staff.",
  "metadata": {
    "data_source": "https://lib.reviews",
    "nickname": "original_author",
    "license": "CC-BY-SA-4.0",
    "is_personal_experience": true,
    "osm_id": "node/12345678/4"
  }
}

14. Generated Review

{
  "iat": 1708099450,
  "sub": "urn:lei:5493006MHB84DD0ZWV18",
  "rating": 75,
  "opinion": "Based on analysis of multiple data sources, this company shows consistent performance in its sector.",
  "metadata": {
    "nickname": "AnalyticsBot",
    "is_generated": true,
    "data_source": "https://analytics.example.com/reports/company-analysis",
    "reviewer_index": 42
  }
}

Validation Rules

Common Validation Errors

  1. Missing Required Fields

    • Error: “Missing required field: sub”
    • Solution: Ensure both sub and iat are present
  2. Missing Conditional Fields

    • Error: “Payload must include at least one of: rating, opinion, or action”
    • Solution: Add at least one of these fields
  3. Invalid URI Scheme

    • Error: “Invalid URI scheme. Must be one of: https, geo, urn:lei, urn:isbn, urn:maresi”
    • Solution: Use a supported URI scheme
  4. Rating Out of Range

    • Error: “Rating must be between 0 and 100”
    • Solution: Ensure rating is an integer from 0 to 100
  5. Opinion Too Long

    • Error: “Opinion exceeds maximum length of 1000 characters”
    • Solution: Shorten the opinion text
  6. Invalid Geo URI

    • Error: “Invalid geo URI format”
    • Solution: Use format geo:lat,lon?q=name&u=radius
  7. Invalid Action

    • Error: “Action must be one of: delete, edit, report_abuse”
    • Solution: Use a valid action value

Best Practices

  1. Always validate before signing

    • Use validation libraries or the JWT Inspector tool
    • Check for required fields and proper formats
  2. Use appropriate URI schemes

    • Choose the scheme that best matches your subject
    • Ensure URIs are properly formatted
  3. Keep opinions concise

    • Focus on key points within the 1000 character limit
    • Use images to supplement text when needed
  4. Include relevant metadata

    • Add context that helps readers understand your perspective
    • Use standard fields when possible
  5. Handle actions carefully

    • Only use actions on reviews you own (except report_abuse)
    • Include clear reasons when reporting abuse

Integration Guide

Tools and Resources

The JWT Inspector builds a payload from example templates, signs it, and checks it against the Mangrove Review Standard.

The ecosystem page lists the libraries, APIs and other tools. Integration guides are at open-reviews.net/technology.

Quick Start Code

import { generateKeypair, signAndSubmitReview } from 'mangrove-reviews';

// Generate a keypair; store it to keep the same reviewer identity
const keypair = await generateKeypair();

// Sign and submit; the library sets `iat` itself
await signAndSubmitReview(keypair, {
  sub: "https://example.com",
  rating: 90,
  opinion: "Excellent service!",
  metadata: {
    nickname: "HappyCustomer"
  }
});

FAQ

General Questions

Q: Can I submit a review without a rating or opinion?
A: No, you must include at least one of: rating, opinion, or action.

Q: What’s the difference between nickname and preferred_username?
A: nickname is a display name (e.g., “John Smith”), while preferred_username is an account identifier (e.g., “jsmith123”).

Q: Can I review the same subject multiple times?
A: Yes, but each review should differ in sub, rating, or opinion. Use the edit action to update existing reviews.

Q: How do I reference my own review for editing?
A: Use the JWT signature from your original review as the MaReSi URI: urn:maresi:[signature]

Technical Questions

Q: Why use JWT instead of a simpler format?
A: JWTs provide cryptographic signatures, ensuring reviews can’t be tampered with and maintaining reviewer identity without central control.

Q: Can I use my own image hosting?
A: Yes, any publicly accessible URL works for image sources.

Q: What happens to deleted reviews?
A: The server marks them as deleted but may retain them for audit purposes. Clients should not display deleted reviews.

Q: How do I validate a geo URI?
A: Ensure it follows the pattern geo:latitude,longitude?q=name&u=radius with valid coordinates and URL-encoded place names.

Troubleshooting

Issue: “Invalid signature” error

  • Ensure you’re using the same private key that generated the public key
  • Check that the JWT hasn’t been modified after signing

Issue: “Timestamp too old” error

  • Some servers may reject reviews with timestamps too far in the past
  • Use current timestamp: Math.floor(Date.now() / 1000)

Issue: Geo URI not working

  • URL-encode the place name: encodeURIComponent("Joe's Pizza") → Joe%27s%20Pizza
  • Ensure coordinates are in decimal format

Issue: Images not displaying

  • Verify image URLs are publicly accessible
  • Check that URLs use HTTPS (some clients require secure connections)

For more details, see the Mangrove Review Standard specification.