2026-10-04 02:57:58 +00:00
"""Browser-only opaque storage. Deliberately has no crypto or MCP registration."""
from __future__ import annotations
import base64
import binascii
import json
import os
import re
2026-10-04 08:05:18 +00:00
import secrets
import hashlib
import time
2026-10-04 02:57:58 +00:00
import sqlite3
from urllib.parse import urlsplit
from contextlib import closing
2026-10-04 08:05:18 +00:00
from starlette.requests import Request
2026-10-04 02:57:58 +00:00
from starlette.responses import JSONResponse , Response
from starlette.routing import Route
EXTENSION_ORIGIN = "chrome-extension://kiolgjoohhgdohcafgelhikpgkbmckbg"
PREFIX = "/chat/persimmons/human-vault/items"
MAX_BODY = 24 * 1024
ITEM_ID = re . compile ( r "[0-9a-f] {32} " )
2026-10-04 08:05:18 +00:00
CAPABILITY = re . compile ( r "[A-Za-z0-9_-]{40,128}" )
CAPABILITY_TTL = 30 * 24 * 60 * 60
DEFAULT_DB = "/data/human-vault.sqlite"
def storage_path ():
return os . environ . get ( "PERSIMMONS_HUMAN_VAULT_DB" , DEFAULT_DB )
2026-10-04 02:57:58 +00:00
def validate_item ( item ):
if not isinstance ( item , dict ) or set ( item ) not in ({ "v" , "id" , "origin" , "iv" , "ciphertext" }, { "v" , "id" , "origin" , "username" , "iv" , "ciphertext" }):
raise ValueError ()
if type ( item [ "v" ]) is not int or item [ "v" ] != 1 :
raise ValueError ()
if not isinstance ( item [ "id" ], str ) or not ITEM_ID . fullmatch ( item [ "id" ]):
raise ValueError ()
if not isinstance ( item [ "origin" ], str ):
raise ValueError ()
try :
parsed = urlsplit ( item [ "origin" ])
_ = parsed . port
except ValueError :
raise ValueError ()
if ( parsed . scheme != "https" or not parsed . hostname or parsed . username or parsed . password or
parsed . path or parsed . query or parsed . fragment or parsed . geturl () != item [ "origin" ]):
raise ValueError ()
username = item . get ( "username" )
if username is not None and ( not isinstance ( username , str ) or not username or len ( username ) > 512 or any ( ord ( c ) < 32 for c in username )):
raise ValueError ()
for name , low , high in (( "iv" , 12 , 12 ), ( "ciphertext" , 17 , 16384 )):
value = item [ name ]
if not isinstance ( value , str ):
raise ValueError ()
raw = base64 . b64decode ( value , validate = True )
if not low <= len ( raw ) <= high or base64 . b64encode ( raw ) . decode () != value :
raise ValueError ()
return item
class Store :
def __init__ ( self , path ):
self . path = path
2026-10-04 08:05:18 +00:00
def _cap_table ( self , db ):
db . execute ( "CREATE TABLE IF NOT EXISTS human_capabilities (owner TEXT NOT NULL, token_hash TEXT NOT NULL PRIMARY KEY, expires_at INTEGER NOT NULL)" )
db . execute ( "DELETE FROM human_capabilities WHERE expires_at < ?" , ( int ( time . time ()),))
def mint_capability ( self , owner ):
token = secrets . token_urlsafe ( 32 )
digest = hashlib . sha256 ( token . encode ()) . hexdigest ()
expires_at = int ( time . time ()) + CAPABILITY_TTL
with closing ( sqlite3 . connect ( self . path , timeout = 5 )) as db , db :
self . _cap_table ( db )
db . execute ( "DELETE FROM human_capabilities WHERE owner=?" , ( owner ,))
db . execute ( "INSERT INTO human_capabilities (owner,token_hash,expires_at) VALUES (?,?,?)" , ( owner , digest , expires_at ))
return token , expires_at
def capability_owner ( self , token ):
if not isinstance ( token , str ) or not CAPABILITY . fullmatch ( token ):
return None
digest = hashlib . sha256 ( token . encode ()) . hexdigest ()
with closing ( sqlite3 . connect ( self . path , timeout = 5 )) as db , db :
self . _cap_table ( db )
row = db . execute ( "SELECT owner FROM human_capabilities WHERE token_hash=? AND expires_at>=?" , ( digest , int ( time . time ()))) . fetchone ()
return row [ 0 ] if row else None
2026-10-04 02:57:58 +00:00
def operate ( self , owner , method , item_id = None , item = None ):
with closing ( sqlite3 . connect ( self . path , timeout = 5 )) as db , db :
db . execute ( "CREATE TABLE IF NOT EXISTS human_items (owner TEXT NOT NULL, id TEXT NOT NULL, origin TEXT NOT NULL, iv TEXT NOT NULL, ciphertext TEXT NOT NULL, username TEXT, PRIMARY KEY(owner,id))" )
columns = { row [ 1 ] for row in db . execute ( "PRAGMA table_info(human_items)" )}
if "username" not in columns :
db . execute ( "ALTER TABLE human_items ADD COLUMN username TEXT" )
if method == "POST" :
if db . execute ( "SELECT count(*) FROM human_items WHERE owner=?" , ( owner ,)) . fetchone ()[ 0 ] >= 200 :
return { "error" : "item limit reached" }, 409
try :
db . execute ( "INSERT INTO human_items (owner,id,origin,iv,ciphertext,username) VALUES (?,?,?,?,?,?)" , ( owner , item [ "id" ], item [ "origin" ], item [ "iv" ], item [ "ciphertext" ], item . get ( "username" )))
except sqlite3 . IntegrityError :
return { "error" : "item already exists" }, 409
elif method == "PUT" :
changed = db . execute ( "UPDATE human_items SET origin=?,iv=?,ciphertext=?,username=COALESCE(?,username) WHERE owner=? AND id=?" , ( item [ "origin" ], item [ "iv" ], item [ "ciphertext" ], item . get ( "username" ), owner , item_id ))
if not changed . rowcount :
return { "error" : "not found" }, 404
elif method == "DELETE" :
changed = db . execute ( "DELETE FROM human_items WHERE owner=? AND id=?" , ( owner , item_id ))
if not changed . rowcount :
return { "error" : "not found" }, 404
return { "owner" : owner , "ok" : True }, 200
elif item_id is None :
rows = db . execute ( "SELECT id,origin,username FROM human_items WHERE owner=? ORDER BY origin,username,id LIMIT 200" , ( owner ,)) . fetchall ()
return { "owner" : owner , "items" : [{ "v" : 1 , "id" : row [ 0 ], "origin" : row [ 1 ], ** ({ "username" : row [ 2 ]} if row [ 2 ] else {})} for row in rows ]}, 200
else :
row = db . execute ( "SELECT origin,iv,ciphertext,username FROM human_items WHERE owner=? AND id=?" , ( owner , item_id )) . fetchone ()
if row is None :
return { "error" : "not found" }, 404
item = dict ( v = 1 , id = item_id , origin = row [ 0 ], iv = row [ 1 ], ciphertext = row [ 2 ], ** ({ "username" : row [ 3 ]} if row [ 3 ] else {}))
return { "owner" : owner , "item" : item }, 201 if method == "POST" else 200
def routes ( resolve_actor ):
async def endpoint ( request ):
headers = { "Cache-Control" : "no-store" , "Vary" : "Origin" }
def reply ( payload , code ):
return JSONResponse ( payload , status_code = code , headers = headers )
# Do not trust forwarded headers here; the ASGI server's trusted proxy
# configuration owns scheme handling. No bearer/PAT fallback.
if request . url . scheme != "https" or request . headers . getlist ( "origin" ) != [ EXTENSION_ORIGIN ]:
return reply ({ "error" : "forbidden" }, 403 )
headers . update ({ "Access-Control-Allow-Origin" : EXTENSION_ORIGIN , "Access-Control-Allow-Credentials" : "true" })
if request . url . query :
return reply ({ "error" : "query forbidden" }, 400 )
if request . method == "OPTIONS" :
requested = { h . strip () . lower () for h in request . headers . get ( "access-control-request-headers" , "" ) . split ( "," ) if h . strip ()}
2026-10-04 08:05:18 +00:00
if request . headers . get ( "access-control-request-method" ) not in { "GET" , "POST" , "PUT" , "DELETE" } or not requested <= { "content-type" , "x-vault-owner" , "x-vault-capability" }:
2026-10-04 02:57:58 +00:00
return reply ({ "error" : "forbidden" }, 403 )
2026-10-04 08:05:18 +00:00
return Response ( status_code = 204 , headers = { ** headers , "Access-Control-Allow-Methods" : "GET, POST, PUT, DELETE" , "Access-Control-Allow-Headers" : "Content-Type, X-Vault-Owner, X-Vault-Capability" })
path = storage_path ()
if not path :
return reply ({ "error" : "vault not configured" }, 503 )
owner = Store ( path ) . capability_owner ( request . headers . get ( "x-vault-capability" ))
if not owner :
if not request . headers . get ( "cookie" ) or request . headers . get ( "authorization" ):
return reply ({ "error" : "unauthorized" }, 401 )
try :
actor = await resolve_actor ( request )
except Exception :
return reply ({ "error" : "session unavailable" }, 503 )
if not actor :
return reply ({ "error" : "unauthorized" }, 401 )
owner = actor . lower ()
2026-10-04 02:57:58 +00:00
item_id = request . path_params . get ( "item_id" )
if item_id is not None and not ITEM_ID . fullmatch ( item_id ):
return reply ({ "error" : "not found" }, 404 )
if ( item_id is not None or request . method != "GET" ) and request . headers . get ( "x-vault-owner" ) != owner :
return reply ({ "error" : "session changed" }, 409 )
item = None
if request . method in { "POST" , "PUT" }:
if request . headers . get ( "content-type" , "" ) . split ( ";" )[ 0 ] != "application/json" :
return reply ({ "error" : "JSON required" }, 415 )
try :
raw = bytearray ()
async for chunk in request . stream ():
raw . extend ( chunk )
if len ( raw ) > MAX_BODY :
raise ValueError ()
item = validate_item ( json . loads ( raw ))
if item_id is not None and item [ "id" ] != item_id :
raise ValueError ()
except ( ValueError , TypeError , binascii . Error ):
return reply ({ "error" : "invalid encrypted item" }, 400 )
try :
payload , code = Store ( path ) . operate ( owner , request . method , item_id , item )
except ( sqlite3 . Error , OSError ):
return reply ({ "error" : "vault unavailable" }, 503 )
return reply ( payload , code )
2026-10-04 08:05:18 +00:00
async def read_endpoint ( request ):
if request . method == "OPTIONS" :
return await endpoint ( request )
async for chunk in request . stream ():
if chunk :
return JSONResponse ({ "error" : "read body must be empty" }, status_code = 400 )
read_request = Request ({ ** request . scope , "method" : "GET" }, receive = request . receive )
return await endpoint ( read_request )
async def link_endpoint ( request ):
headers = { "Cache-Control" : "no-store" }
if request . url . scheme != "https" or request . headers . get ( "origin" ) != "https://persimmons.app" :
return JSONResponse ({ "error" : "forbidden" }, status_code = 403 , headers = headers )
if request . method != "POST" or request . headers . get ( "authorization" ) or not request . headers . get ( "cookie" ) or request . headers . get ( "x-requested-with" ) != "Persimmons" :
return JSONResponse ({ "error" : "unauthorized" }, status_code = 401 , headers = headers )
try :
actor = await resolve_actor ( request )
except Exception :
return JSONResponse ({ "error" : "session unavailable" }, status_code = 503 , headers = headers )
if not actor :
return JSONResponse ({ "error" : "unauthorized" }, status_code = 401 , headers = headers )
path = storage_path ()
if not path :
return JSONResponse ({ "error" : "vault not configured" }, status_code = 503 , headers = headers )
try :
token , expires_at = Store ( path ) . mint_capability ( actor . lower ())
except ( sqlite3 . Error , OSError ):
return JSONResponse ({ "error" : "vault unavailable" }, status_code = 503 , headers = headers )
return JSONResponse ({ "owner" : actor . lower (), "capability" : token , "expires_at" : expires_at }, headers = headers )
return [
Route ( PREFIX , endpoint , methods = [ "GET" , "POST" , "OPTIONS" ]),
Route ( PREFIX + "/ {item_id} " , endpoint , methods = [ "GET" , "PUT" , "DELETE" , "OPTIONS" ]),
Route ( PREFIX + "/read" , read_endpoint , methods = [ "POST" , "OPTIONS" ]),
Route ( PREFIX + "/ {item_id} /read" , read_endpoint , methods = [ "POST" , "OPTIONS" ]),
Route ( "/chat/persimmons/human-vault/link" , link_endpoint , methods = [ "POST" ]),
]