feat(ai): AI Data Access grants live on the node, not in localStorage
Operator: "the AI Data Access settings are not persistent through sessions, often turns them all off." They were stored in localStorage, which is scoped to an ORIGIN — and a node answers on several: LAN address, Tailscale address, <host>.local, hostname. Granting Media over the LAN and returning over Tailscale showed every switch off again. Not reset: never set *there*. It also made a working content path look broken, because every scope silently returns nothing without a grant, so an ungranted permission is indistinguishable from an empty library — that is exactly what an empty films search turned out to be. The grant answers "what may the assistant read about THIS NODE", which is a property of the node, not of one browser at one address. New settings/ai_permissions.rs (same shape as session_policy: atomic temp+rename, sanitised on read and write, fails closed on a corrupt file — an unreadable grant file must never read as "everything allowed"). New ai.permissions.get / .set, absent from the unauthenticated allowlist so they require a session. Migration, not replacement: if this browser holds grants and the node holds none, the local set is pushed UP rather than wiped. Without that, upgrading would silently revoke the grants of everyone who set them before this change. The node still wins in every other direction, so a revocation made on one device takes effect everywhere — otherwise revoking would be impossible from a second device. Unknown category ids are stored verbatim rather than validated against a hardcoded list: a third copy of that list would silently drop a new category on upgrade. Storing a category grants nothing by itself — the broker checks before fetching and the node re-checks before answering (T-13-33). Hydration happens ONCE at broker start, not inside each permission gate: the gates are hot-path, and awaiting there adds an RPC to every content and context request. The first attempt did it per-gate and the existing broker tests caught it by failing on consumed mocks. Rust 7/7, store 18/18, broker 23/23. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9e86d18921
commit
762c72b4d0
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAIPermissionsStore, AI_PERMISSION_CATEGORIES } from '@/stores/aiPermissions'
|
||||
import ToggleSwitch from '@/components/ToggleSwitch.vue'
|
||||
@@ -7,6 +7,12 @@ import ToggleSwitch from '@/components/ToggleSwitch.vue'
|
||||
const { t } = useI18n()
|
||||
const aiPermissions = useAIPermissionsStore()
|
||||
|
||||
// Grants live on the node, not in this browser's localStorage — reconcile on
|
||||
// open so the switches show the node's truth rather than whatever this origin
|
||||
// happens to remember. Without this the same node shows different settings at
|
||||
// its LAN address and its Tailscale address.
|
||||
onMounted(() => { void aiPermissions.hydrate() })
|
||||
|
||||
const aiCategoryGroups = computed(() => {
|
||||
const groups: { label: string; items: typeof AI_PERMISSION_CATEGORIES }[] = []
|
||||
for (const cat of AI_PERMISSION_CATEGORIES) {
|
||||
|
||||
Reference in New Issue
Block a user