···11+CREATE TABLE IF NOT EXISTS delegated_accounts (
22+ account_did TEXT PRIMARY KEY,
33+ linked_by TEXT NOT NULL,
44+ api_client_id TEXT NOT NULL,
55+ created_at TEXT NOT NULL
66+);
···11+CREATE TABLE IF NOT EXISTS account_delegates (
22+ account_did TEXT NOT NULL REFERENCES delegated_accounts(account_did) ON DELETE CASCADE,
33+ user_did TEXT NOT NULL,
44+ role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
55+ granted_by TEXT NOT NULL,
66+ created_at TEXT NOT NULL,
77+ PRIMARY KEY (account_did, user_did)
88+);
···11+CREATE TABLE IF NOT EXISTS delegated_accounts (
22+ account_did TEXT PRIMARY KEY,
33+ linked_by TEXT NOT NULL,
44+ api_client_id TEXT NOT NULL,
55+ created_at TEXT NOT NULL
66+);
···11+CREATE TABLE IF NOT EXISTS account_delegates (
22+ account_did TEXT NOT NULL REFERENCES delegated_accounts(account_did) ON DELETE CASCADE,
33+ user_did TEXT NOT NULL,
44+ role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
55+ granted_by TEXT NOT NULL,
66+ created_at TEXT NOT NULL,
77+ PRIMARY KEY (account_did, user_did)
88+);
···4343 lexicon: &ParsedLexicon,
4444 script: &str,
4545 space_ctx: Option<&context::SpaceContext>,
4646+ delegate_did: Option<&str>,
4647) -> Result<Response, AppError> {
4748 let start = Instant::now();
4849 let backend = state.db_backend;
···253254 return Err(AppError::Internal(error_message));
254255 }
255256256256- if let Err(e) = record::register_record_api(&lua, state_arc, claims_arc, pds_auth_arc) {
257257+ if let Err(e) = record::register_record_api(
258258+ &lua,
259259+ state_arc,
260260+ claims_arc,
261261+ pds_auth_arc,
262262+ delegate_did.map(|s| s.to_string()),
263263+ ) {
257264 let error_message = format!("failed to register Record API: {e}");
258265 log_event(
259266 &state.db,
···285292 claims.did(),
286293 collection,
287294 space_ctx,
295295+ delegate_did,
288296 ) {
289297 let error_message = format!("failed to set context: {e}");
290298 log_event(
+23-3
src/lua/record.rs
···23232424/// Register the `Record` global constructor and static methods.
2525/// Only registered for procedure scripts (not queries).
2626+///
2727+/// When `delegate_did` is `Some`, record writes default to the delegate's repo
2828+/// instead of the caller's DID. Scripts can still override via `record:set_repo()`.
2629pub fn register_record_api(
2730 lua: &Lua,
2831 state: Arc<AppState>,
2932 claims: Arc<Claims>,
3033 pds_auth: Arc<PdsAuth>,
3434+ delegate_did: Option<String>,
3135) -> LuaResult<()> {
3236 // -- methods table (shared by all Record instances) --
3337 let methods = lua.create_table()?;
···3741 let state = state.clone();
3842 let claims = claims.clone();
3943 let pds_auth = pds_auth.clone();
4444+ let delegate_did = delegate_did.clone();
4045 let save_fn = lua.create_async_function(move |lua, this: mlua::Table| {
4146 let state = state.clone();
4247 let claims = claims.clone();
4348 let pds_auth = pds_auth.clone();
4949+ let delegate_did = delegate_did.clone();
4450 async move {
4551 let backend = state.db_backend;
4652 let collection: String = this.raw_get("_collection")?;
4753 let schema: mlua::Value = this.raw_get("_schema")?;
4854 let repo_override: Option<String> = this.raw_get("_repo_override")?;
4949- let repo = repo_override.as_deref().unwrap_or_else(|| claims.did());
5555+ let repo = repo_override
5656+ .as_deref()
5757+ .or(delegate_did.as_deref())
5858+ .unwrap_or_else(|| claims.did());
50595160 // Validate required fields against schema
5261 if let mlua::Value::Table(ref schema_table) = schema {
···207216 let state = state.clone();
208217 let claims = claims.clone();
209218 let pds_auth = pds_auth.clone();
219219+ let delegate_did = delegate_did.clone();
210220 let delete_fn = lua.create_async_function(move |_lua, this: mlua::Table| {
211221 let state = state.clone();
212222 let claims = claims.clone();
213223 let pds_auth = pds_auth.clone();
224224+ let delegate_did = delegate_did.clone();
214225 async move {
215226 let backend = state.db_backend;
216227 let uri: String = this.raw_get::<Option<String>>("_uri")?.ok_or_else(|| {
···218229 })?;
219230 let collection: String = this.raw_get("_collection")?;
220231 let repo_override: Option<String> = this.raw_get("_repo_override")?;
221221- let repo = repo_override.as_deref().unwrap_or_else(|| claims.did());
232232+ let repo = repo_override
233233+ .as_deref()
234234+ .or(delegate_did.as_deref())
235235+ .unwrap_or_else(|| claims.did());
222236223237 let rkey = uri
224238 .split('/')
···439453 let state = state.clone();
440454 let claims = claims.clone();
441455 let pds_auth = pds_auth.clone();
456456+ let delegate_did = delegate_did.clone();
442457 let save_all_fn =
443458 lua.create_async_function(move |lua, records_table: mlua::Table| {
444459 let state = state.clone();
445460 let claims = claims.clone();
446461 let pds_auth = pds_auth.clone();
462462+ let delegate_did = delegate_did.clone();
447463 async move {
448464 let backend = state.db_backend;
449465 // Extract save data from each record (sync)
···472488 let state = state.clone();
473489 let claims = claims.clone();
474490 let pds_auth = pds_auth.clone();
491491+ let delegate_did = delegate_did.clone();
475492 let collection = collection.clone();
476493 let existing_uri = existing_uri.clone();
477494 let rkey = rkey.clone();
478495 let repo_override = repo_override.clone();
479496 let data = data.clone();
480497 async move {
481481- let repo = repo_override.as_deref().unwrap_or_else(|| claims.did());
498498+ let repo = repo_override
499499+ .as_deref()
500500+ .or(delegate_did.as_deref())
501501+ .unwrap_or_else(|| claims.did());
482502 if let Some(ref uri) = existing_uri {
483503 let rkey = uri
484504 .split('/')