The problem
Most gear review sites hide how their scores are made, mix "we tested it" with "we read about it", and send clicks through redirects anyone can abuse.
Travistello is the opposite bet: a catalog where every product, variant, spec, source and score is a typed record, and where the platform itself refuses to publish anything it cannot back up.
From raw evidence to a click
- 01Product + variant
- 02Evidence sources
- 03Versioned score
- 04Offer
- 05Compare
- 06Tracked redirect
Trust and security rules in the code
| RULE | LAYER | PREVENTS |
|---|---|---|
| Opaque /go/[token] redirects with server-owned destinations | Routing | Open redirect |
| Outbound clicks store no IP address, bots are filtered out | Analytics | Tracking abuse |
| Score weights must total 100%, the overall score is derived | Data model | Hand-tuned scores |
| Research-based and hands-on reviews are distinct data states | Data model | Fake reviews |
| Every media record needs rights owner and usage basis | CMS | Licensing issues |
| Affiliate programs and click data restricted to admins | CMS roles | Data exposure |
Straight from the code
Excerpts from the actual repository and deliverables, trimmed for length.
export const getSafeOutboundURL = (value: null | string | undefined) => {
if (!value) return null;
try {
const url = new URL(value);
if (
!["http:", "https:"].includes(url.protocol) ||
url.username ||
url.password
)
return null;
return url;
} catch {
return null;
}
};Every outbound link goes through this check: only http(s), never embedded credentials. Destinations come from the database, never from the URL.
export async function GET(request: NextRequest, { params }: RouteContext) {
const { token } = await params;
const offers = await payload.find({
collection: "offers",
limit: 1,
where: {
and: [
{ publicToken: { equals: token } },
{ status: { equals: "active" } },
],
},
});
const offer = offers.docs[0];
if (!offer || !isPublicOffer(offer))
return new NextResponse("Offer not found.", { status: 404 });
const destination = getSafeOutboundURL(offer.affiliateUrl || offer.destinationUrl);
if (!destination)
return new NextResponse("Offer destination is unavailable.", { status: 503 });
const bot = isLikelyBot(request.headers.get("user-agent"));
// ... only non-bot clicks are recorded, with no IP address
}The /go/[token] redirect: the token is looked up server-side, inactive offers return 404, and bots never create click records.
Results
What I'd do differently
The hard part was never the features, it was the trust rules: deciding what the platform must refuse to show. Next: activate affiliate programs and add the first hands-on reviews as their own data state.