private rover security! and styling updaes

This commit is contained in:
legop3
2026-07-06 15:26:43 -04:00
parent f9f87c00d3
commit 07ad43f42f
9 changed files with 45 additions and 28 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -78,8 +78,8 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script> <script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script> <script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title> <title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-qmfprxrq.js"></script> <script type="module" crossorigin src="/assets/index-_MGdcdqA.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CjRD7Org.css"> <link rel="stylesheet" crossorigin href="/assets/index-LIjDMlbF.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
@@ -137,11 +137,26 @@ function buildRoomCameraInfo(camera) {
}; };
} }
function isClosedPrivateRover(rover) {
return Boolean(rover?.private?.enabled && !rover?.private?.open);
}
function getPublicRoster() {
return roverManager
.getRoster()
/*
Closed private rovers are intentionally absent from the public
inter-instance contract. If a rover is private and closed, other servers
should not see its row or receive any derived snapshot URL for it.
*/
.filter((rover) => !isClosedPrivateRover(rover));
}
function buildLocalInfo() { function buildLocalInfo() {
const mode = getMode(); const mode = getMode();
const lockdown = isLockdownMode(); const lockdown = isLockdownMode();
const features = getFeatureFlags(); const features = getFeatureFlags();
const roster = roverManager.getRoster().map((rover) => (lockdown ? rover : addRoverSnapshotLinks(rover))); const roster = getPublicRoster().map((rover) => (lockdown ? rover : addRoverSnapshotLinks(rover)));
const roomCameras = lockdown || !features.roomCameras ? [] : getRoomCameras().map(buildRoomCameraInfo); const roomCameras = lockdown || !features.roomCameras ? [] : getRoomCameras().map(buildRoomCameraInfo);
return { return {
instance: { instance: {
@@ -184,6 +199,16 @@ app.get('/api/inter-instance/rover-snapshots/:roverId/latest', (req, res) => {
return; return;
} }
const roverId = String(req.params.roverId || ''); const roverId = String(req.params.roverId || '');
const publicRover = getPublicRoster().find((rover) => String(rover.id) === roverId);
if (!publicRover) {
/*
Do not rely on "not advertising the URL" as the privacy boundary. Public
snapshot hosting must also reject direct requests for closed-private or
unknown rovers because old URLs, logs, or guesses can outlive roster state.
*/
res.status(404).json({ error: 'Rover snapshot unavailable' });
return;
}
sendJpegState(res, getRoverSnapshotState(roverId), 'Rover snapshot unavailable'); sendJpegState(res, getRoverSnapshotState(roverId), 'Rover snapshot unavailable');
}); });
@@ -94,29 +94,13 @@ function InstanceMetadata({ remote }) {
} }
function RemoteMediaStrip({ remote }) { function RemoteMediaStrip({ remote }) {
const roverSnapshots = (remote.roster || [])
.map((rover) => ({
id: rover.id,
name: rover.name || rover.id,
url: rover.snapshots?.latestUrl,
updatedAt: rover.snapshots?.updatedAt,
}))
.filter((entry) => entry.url);
const roomCameras = Array.isArray(remote.roomCameras) ? remote.roomCameras.filter((camera) => camera.snapshotUrl) : []; const roomCameras = Array.isArray(remote.roomCameras) ? remote.roomCameras.filter((camera) => camera.snapshotUrl) : [];
const items = [ if (!roomCameras.length) return null;
...roverSnapshots.map((entry) => ({ ...entry, kind: 'Rover' })),
...roomCameras.map((entry) => ({ ...entry, name: entry.name || entry.id, url: entry.snapshotUrl, kind: 'Room' })),
];
if (!items.length) return null;
return ( return (
<div className="grid grid-cols-2 gap-0.5 md:grid-cols-3"> <div className="flex gap-0.5 overflow-x-auto pb-0.5">
{items.map((item) => ( {roomCameras.map((camera) => (
<div key={`${item.kind}-${item.id}`} className="surface-muted overflow-hidden text-xs"> <div key={camera.id} className="surface-muted w-28 shrink-0 overflow-hidden">
<img src={item.url} alt={item.name} className="aspect-video w-full bg-black object-cover" loading="lazy" /> <img src={camera.snapshotUrl} alt={camera.name || camera.id} className="h-14 w-full bg-black object-cover" loading="lazy" />
<div className="flex items-center justify-between gap-0.5 p-0.5">
<span className="truncate text-slate-200">{item.name}</span>
<span className="text-[0.65rem] text-slate-500">{item.kind}</span>
</div>
</div> </div>
))} ))}
</div> </div>
@@ -229,6 +229,14 @@ export default function RoverQueuesPanel({
handleRequest(rover.id); handleRequest(rover.id);
}} }}
> >
{externalMode && rover?.snapshots?.latestUrl ? (
<img
src={rover.snapshots.latestUrl}
alt=""
className="h-8 w-10 shrink-0 rounded border border-slate-700 bg-black object-cover"
loading="lazy"
/>
) : null}
<div className="flex min-w-0 flex-1 flex-col gap-0.5"> <div className="flex min-w-0 flex-1 flex-col gap-0.5">
<div className="flex items-center justify-between gap-0.5"> <div className="flex items-center justify-between gap-0.5">
<div className="flex min-w-0 items-center gap-0.5"> <div className="flex min-w-0 items-center gap-0.5">