Order Zero Phase 1: keyed read-only PropLine verification endpoint
Adds GET /api/internal/propline-verify (internal-key gated, read-only) so Phase 1 can run WHERE THE KEY LIVES. Touches no cache, no ledger, no grade; the live adapter and the live ruler are untouched. Breadth reuses proplineAdapter.fetchRaw -- the exact live request -- so what it measures is what the pipeline actually receives. Reports per sport (never pooled): books/prop from the feed vs after our own ALLOWED_BOOKS, props made INVISIBLE by that filter, reference-book presence, DFS presence reported separately, and consensus eligibility. Consensus eligibility is deliberately strict: >=2 REFERENCE books posting BOTH sides at the SAME line. A one-sided quote cannot be de-vigged, and two books at different lines are not the same market -- counting either would overstate how much of the slate can carry a real ruler. Probes the documented-but-unverified endpoints (/sports, /context, /odds/closing, /movement, /results, /exports/resolved-props for four sport keys) and classifies works/partial/no, with 403 = tier-gated and 200-but- empty = partial rather than works. Key safety is the other locked property: the key goes via axios params, never string-interpolated, and every emitted string passes scrubKeys() which removes the literal key AND any surviving apiKey= query value. A test asserts a thrown transport error carrying the key cannot escape. 13 unit tests, hermetic (no network, no key). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
@@ -528,4 +528,32 @@ router.post('/outcomes/:sport', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/internal/propline-verify (Order Zero, Phase 1)
|
||||
*
|
||||
* KEYED, READ-ONLY verification — runs where the PropLine key lives. Reports
|
||||
* per-sport book breadth (feed vs after our own allow-list), exchange reality,
|
||||
* consensus eligibility, and works/partial/no for the documented-but-unverified
|
||||
* endpoints. Touches no cache, no ledger, no grade; the live adapter and the
|
||||
* live ruler are untouched.
|
||||
*
|
||||
* Costs ~1 PropLine call per sport plus one per probe. Never returns the key —
|
||||
* every string it emits passes through `scrubKeys`.
|
||||
*
|
||||
* ?sports=mlb,wnba (default: mlb,wnba)
|
||||
*/
|
||||
router.get('/propline-verify', async (req, res) => {
|
||||
try {
|
||||
const sports = String(req.query.sports || 'mlb,wnba')
|
||||
.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean).slice(0, 6);
|
||||
const { verify } = require('../services/proplineVerify');
|
||||
const out = await verify({ sports });
|
||||
res.set('Cache-Control', 'no-store');
|
||||
return res.json({ ok: true, ...out });
|
||||
} catch (err) {
|
||||
const { __internals } = require('../services/proplineVerify');
|
||||
return res.status(500).json({ ok: false, error: __internals.scrubKeys(err && err.message) });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user