# Public repositories on authenticated storage

An authenticated storage server can host private and public repositories on the
same TLS endpoint. Repositories default to private. Public visibility permits
anonymous discovery, metadata/history/branch reads, and download of accepted file
content. It exposes every branch and all retained accepted history, including
history accepted before the repository became public. It does not grant uploads,
submits, repository creation, branch mutation, locks, workspace registration,
operation outcomes, or administration.

Visibility belongs to shared auth policy, keyed by namespace and immutable
repository ID. It is not versioned content in `.scribe/config.toml`. Changing it
requires an auth administrator, independently of repository roles. Existing
explicit roles and namespace administration continue to govern authenticated work.

## Publish and restrict access

Use the administrator credential file and pending-state options described in
the [auth CLI reference](/docs/reference/cli/scribe-auth.md); the credential file
names the auth endpoint and its CA. For example:

```sh
scribe-auth --admin-credential ./secrets/admin.credential visibility get personal/REPOSITORY_ID
scribe-auth --admin-credential ./secrets/admin.credential visibility set personal/REPOSITORY_ID public --expected-revision 0
```

Replace `REPOSITORY_ID` with the full ID from `scribe repo list`. `get` returns
repository key, visibility, and revision. A missing policy is `private` at revision
0. Every successful `set` increments the revision. Use the latest returned revision
to set `private` again. Concurrent updates with an old revision fail with
`base_mismatch`; inspect the new policy before submitting a different intent.

Updates use the existing durable administrative ID, outcome, and audit mechanism.
Retries retain the original outcome; reuse with a different intent is rejected.
Auth can store policy before a repository exists, just as it can store grants.

## Download and later contribute

```sh
scribe --server tls://localhost:7557 --tls-ca ./ca.pem --anonymous repo list
scribe --server tls://localhost:7557 --tls-ca ./ca.pem --anonymous workspace create assets ./assets
cd assets
scribe --tls-ca ../ca.pem --anonymous sync
```

Without a credential file or saved session, TLS connections automatically use
anonymous access: the client presents no certificate. `--anonymous` explicitly
ignores saved sessions and `SCRIBE_CREDENTIAL_FILE`; it conflicts with
`--credential-file`. Supplied invalid, disabled, or expired credentials fail;
there is no automatic retry as anonymous. TLS trust is always required.

Anonymous working copies have only local workspace state. They do not allocate a
persistent guest account or server-side workspace. Local edits remain protected by
the ordinary recovery journal, opened-path deferral, and watermark rules.

After logging in with a user who has write permission, run `scribe workspace
register`. This explicitly registers a fresh workspace identity, preserves files
and opened edits, and allows subsequent submissions. Its identity is persisted
before transmission, so interrupted registration can be retried. A saved anonymous
ID is never evidence of authenticated ownership. Resolve pending operations and
view/switch workflows before registration.

## Enforcement, scale, and revocation

The storage authorization layer enforces both callers through `AuthProvider`.
The gateway forwards a signed-in user's access certificate, which storage checks
against the gateway's delegate grant, or an anonymous connection; it never
substitutes its own credential. Its private inventory is not an authorization
decision. See [gateways and delegation](/docs/auth.md#gateways-and-delegation).

Discovery visits at most 128 repository candidates per request and checks them in
one bounded auth batch. Replies can be empty with a continuation. Secured cursors
are opaque, connection-scoped, and replaced on each continuation; they do not
reveal skipped private IDs. Finish a listing before interleaving other operations.
CLI listing aggregates pages with a one-million-repository/page safety bound.

Storage shares a bounded 1,024-entry public-policy cache and coalesces concurrent
misses for the same key. Decisions last at most 30 seconds, further limited by
credential expiry. After expiry, an unavailable auth service denies new read
admissions. Visibility changes take effect within the same bound; already admitted
bounded requests can finish, and downloaded content cannot be recalled.

Anonymous connections are limited to half the configured connection capacity,
and anonymous requests to half the smallest query/storage/transfer worker pool
(with a minimum of one). Admission, including anonymous handshakes, happens before acquiring shared worker permits.
Anonymous frame bodies can use at most 64 MiB of the shared 128 MiB request budget.
Chunk requests use the ordinary bulk-transfer pool and existing size limits.

Public-only object reads expose accepted manifests. Chunk reads additionally name
their manifest; storage verifies accepted membership and the chunk reference.
One anonymous chunk request may make the server read at most 64 MiB of those
witness manifests; a larger one is refused (`a public chunk read may name at
most 64 MiB of manifests at once; request fewer chunks`).
Uploaded or rejected objects do not become public merely by existing. Explicitly
granted repository readers retain their existing object access. Acceptance records
manifest membership in the same transaction as accepted history, including merges.
`doctor` and `verify` check this index without repairing it.

The unreleased repository schema requires the `accepted_manifests` table; startup
does not silently initialize missing indexes. Object bytes, IDs, and chunking
fixtures are unchanged. See [on-disk format](/docs/on-disk-format.md) and
[wire format](/docs/wire-format.md) for canonical encodings.
