---
summary: "Wire a ScaiDrive share as a knowledge source \u2014 listing accessible shares,\
  \ token exchange, search behaviour, common pitfalls."
title: Attach a ScaiDrive share
path: tutorials/attach-a-scaidrive-share
status: published
---

A persona can read from ScaiDrive shares as well as ScaiMatrix collections. ScaiDrive sources behave a little differently: access is enforced per-call via token exchange against the invoking user's bearer token, not at the persona level. That's the right trade-off for shared drives but it has a few implications you should know about.

## 1. List the shares you can attach

The module exposes a helper that lists shares accessible to the current user, going through token exchange behind the scenes.

```bash
curl "$SCAIGRID_HOST/v1/modules/scaipersona/scaidrive/shares" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"
```

```python
import httpx, os
shares = httpx.get(
    f"{os.environ['SCAIGRID_HOST']}/v1/modules/scaipersona/scaidrive/shares",
    headers={"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"},
).json()["data"]
for s in shares:
    print(s["id"], s.get("name"))
```

```javascript
const res = await fetch(
  `${process.env.SCAIGRID_HOST}/v1/modules/scaipersona/scaidrive/shares`,
  { headers: { "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}` } },
);
const { data: shares } = await res.json();
```

If the deployment isn't wired to ScaiDrive (`scaidrive_base_url` unset in the module config), the endpoint returns `503 SCAIDRIVE_NOT_CONFIGURED`. If token exchange fails (typically because the caller's bearer token can't be exchanged for a ScaiDrive token — e.g. an API key without the right scope), you get `502 SCAIDRIVE_TOKEN_EXCHANGE_FAILED`.

## 2. Attach the share to the persona

```bash
curl -X POST "$SCAIGRID_HOST/v1/modules/scaipersona/personas/$PERSONA_ID/sources" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_type": "scaidrive",
    "source_id": "share_abc123",
    "source_name": "Legal Folder",
    "weight": 1.5
  }'
```

```python
httpx.post(
    f"{os.environ['SCAIGRID_HOST']}/v1/modules/scaipersona/personas/{PERSONA_ID}/sources",
    headers={"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"},
    json={
        "source_type": "scaidrive",
        "source_id": "share_abc123",
        "source_name": "Legal Folder",
        "weight": 1.5,
    },
).raise_for_status()
```

```javascript
await fetch(
  `${process.env.SCAIGRID_HOST}/v1/modules/scaipersona/personas/${PERSONA_ID}/sources`,
  {
    method: "POST",
    headers: { "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      source_type: "scaidrive",
      source_id: "share_abc123",
      source_name: "Legal Folder",
      weight: 1.5,
    }),
  },
);
```

`source_id` is the share id from step 1. `source_name` is what the persona's context labels chunks with ("[Legal Folder]: …") — pick something the model can reason about.

## 3. How retrieval actually runs

For every persona invocation against a ScaiDrive source:

1. The enricher takes the invoking caller's bearer token from the inbound request.
2. Token exchange converts it to a ScaiDrive-scoped token.
3. The exchanged token is sent to ScaiDrive's `/api/v1/search/context` with the user's query, the share id, and a `max_tokens` budget derived from `rag_top_k * 800`.
4. ScaiDrive returns chunks the caller has access to within that share. Anything the caller can't see is filtered out by ScaiDrive — not by ScaiPersona.

The practical consequence: **different callers may get different chunks back from the same persona.** A persona attached to a share with mixed access levels will give richer answers to senior staff than to junior staff. That's a feature for compliance — the persona never elevates privileges — but it can surprise you if you assume every caller sees the same context.

## 4. Tune relevance with `search_config`

You can pass an opaque `search_config` per source. For ScaiDrive sources, the enricher uses `rag_min_score` (the persona-level setting) as the minimum similarity passed to ScaiDrive. There is no per-source min-score in `search_config` today; if you need finer control, split high-precision content into a separate share with a different weight.

## 5. Common pitfalls

**API key callers can't search ScaiDrive.** If your downstream callers authenticate with API keys that don't exchange to ScaiDrive, the share returns no chunks (silently). The persona still answers — just without that source's context. Verify with a JWT-authed test call.

**Shares vs collections behave differently in dry-run.** Collection access is checked against `collection_access_fn` and dropped at retrieval time. ScaiDrive access is enforced inside ScaiDrive after token exchange. There's no admin-side way to preview a share's retrieval for a specific user — you have to invoke as that user.

**Network failures look like empty retrieval.** If ScaiDrive is unreachable or token exchange times out, the enricher logs an exception and returns zero chunks for that source. The persona's answer is still produced but unattributed. Check `scaidrive_search_error` / `persona_rag_token_exchange_error` log lines if a persona suddenly starts hallucinating.

## 6. Mixing collections and shares

A persona can have both source types attached simultaneously. They're searched in parallel, weighted independently, merged into one top-k. Typical pattern: a curated handbook collection as the authoritative source (weight 2.0), and a few wide-net ScaiDrive shares for fresh content (weight 1.0).

If your collection content overlaps a share's content, deduplication catches identical chunks but near-duplicates with different phrasing both survive. Either delete from one source or accept some redundancy in the prompt.
