Plugin to allow users to define their own profiles
Project description
datasette-user-profiles
Plugin to allow users to define their own profiles
Installation
Install this plugin in the same environment as Datasette.
datasette install datasette-user-profiles
Usage
Usage instructions go here.
Locking fields users can't edit
Every profile field is user-editable by default. When another part of your
stack is the authoritative source for a field — e.g. a GitHub auth plugin that
already puts the user's email (and name, avatar, ...) into the actor JSON —
you can stop users from overwriting it from the profile editor.
Set editable_fields in plugin config. Each field defaults to true
(editable); list only the ones you want to lock:
plugins:
datasette-user-profiles:
editable_fields:
email: false
display_name: false
avatar: false # covers the uploaded photo and the generated icon/color
bio: true
Locked fields are enforced server-side — the update and photo endpoints ignore or reject changes to them, not just hide the inputs — and the edit page renders them disabled.
Acting as the actor directory
datasette-user-profiles is the canonical user directory for this stack. Every
"who are the users?" surface — the share dialog's add-a-collaborator box,
@mentions, acl's actor picker, author chips, audit "shared by" lines — draws
from this one indexed, agent-aware directory rather than maintaining its own
ad-hoc list. It exposes three consumer-facing contracts:
1. Search / autocomplete endpoint
GET /-/profiles/api/search?q=<text>&limit=<n>&email=<0|1>
Gated by the profile_access permission (the same gate as every other profile
endpoint). Parameters:
q— free-text query. Matched (case-insensitively) againstdisplay_name,email, andactor_id. Empty/absentqreturns the most-recently-updated profiles instead.limit— max results, defaults to20, capped at50(and floored at1).email— set to0/false/no/offto omit emails from results (defaults to including them).
Display-name prefix matches rank ahead of contains-only matches, then results
are alphabetical by display_name. Response shape:
{
"results": [
{
"id": "alice",
"display_name": "Alice Anderson",
"email": "alice@example.com",
"avatar_url": "/-/profile/pic/alice",
"kind": "user"
}
]
}
kind is always "user" — profiles only knows users. Callers that also want
agents (or other identities) query those sources separately and merge
client-side; profiles stays decoupled from the agent directory.
2. resolve_profile_actors() output shape
resolve_profile_actors(datasette, actor_ids) (see "Actor resolution" below)
returns a {actor_id: {...}} map containing only the IDs that have a profile.
Known users resolve to:
{
"id": "alice",
"display_name": "Alice Anderson",
"email": "alice@example.com",
"kind": "user",
"avatar_url": "/-/profile/pic/alice"
}
IDs without a matching profile are omitted from the map — the caller decides
how to fall back (typically a bare {"id": <id>}).
Consolidation note
This directory replaces three previously-scattered user-listing mechanisms:
| Old mechanism | Now drawn from |
|---|---|
acl datasette_acl_valid_actors (no query, all actors) |
the profiles search API (acl admin UI may keep the hook as a fallback) |
comments datasette_comments_users hook + startswith filtering |
the profiles search API |
comments' private from datasette_user_profiles.routes.pages import get_profile |
resolve_profile_actors(...) |
The old hooks keep working for one release as a fallback when profiles is not installed, then they are retired.
As a cheap convenience, when datasette-acl is installed this plugin also
implements acl's datasette_acl_valid_actors hook, returning every profile as
an {"id", "display"} dict so acl's standalone admin pages get nicer displays.
That hookimpl is only registered if datasette_acl is importable, so profiles
has no hard dependency on acl.
Actor resolution (resolve_profile_actors)
This plugin does not implement Datasette's core actors_from_ids hook.
That hook is declared firstresult=True, so the first plugin to implement it
wins and every other identity source (agents, service accounts, remote
directories) is locked out. Rather than silently seize that hook just by being
installed, profiles exposes its resolution logic as a plain function you can
opt into:
from datasette_user_profiles import resolve_profile_actors
actors = await resolve_profile_actors(datasette, ["alice", "agent-1"])
# {"alice": {"id": "alice", "display_name": "Alice Anderson",
# "email": "alice@example.com", "kind": "user",
# "avatar_url": "/-/profile/pic/alice"}}
It returns a {actor_id: {...}} map for the IDs that have a profile, and omits
the rest so you can merge it with other sources and apply your own fallback.
If you want profiles to back Datasette's core actors_from_ids, wire it up
from a plugin you control — designating a single owner for the hook and
choosing how to merge other identity sources:
from datasette import hookimpl
from datasette_user_profiles import resolve_profile_actors
@hookimpl
def actors_from_ids(datasette, actor_ids):
async def inner():
actors = await resolve_profile_actors(datasette, actor_ids)
# ...merge in agents / service accounts / other directories here...
for actor_id in actor_ids:
actors.setdefault(str(actor_id), {"id": str(actor_id)})
return actors
return inner
Development
To set up this plugin locally, first checkout the code. You can confirm it is available like this:
cd datasette-user-profiles
# Confirm the plugin is visible
uv run datasette plugins
To run the tests:
uv run pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file datasette_user_profiles-0.1.0a7.tar.gz.
File metadata
- Download URL: datasette_user_profiles-0.1.0a7.tar.gz
- Upload date:
- Size: 56.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80d22b24f5142c18c98890b363bc6a3c8c06f2a0155b759c1fcdf64d7ed75320
|
|
| MD5 |
5e1dc6e51e3bb2aadc911e5ff3067fdc
|
|
| BLAKE2b-256 |
0899ce642d0fac5e6a0b965b4ce53d0af351266da0b7f05acf2457ccec1a8cb8
|
Provenance
The following attestation bundles were made for datasette_user_profiles-0.1.0a7.tar.gz:
Publisher:
publish.yml on datasette/datasette-user-profiles
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
datasette_user_profiles-0.1.0a7.tar.gz -
Subject digest:
80d22b24f5142c18c98890b363bc6a3c8c06f2a0155b759c1fcdf64d7ed75320 - Sigstore transparency entry: 1830217337
- Sigstore integration time:
-
Permalink:
datasette/datasette-user-profiles@5533dcecdb8875cea702d5282a7c44c49b8fb5e7 -
Branch / Tag:
refs/tags/0.1.0a7 - Owner: https://github.com/datasette
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5533dcecdb8875cea702d5282a7c44c49b8fb5e7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file datasette_user_profiles-0.1.0a7-py3-none-any.whl.
File metadata
- Download URL: datasette_user_profiles-0.1.0a7-py3-none-any.whl
- Upload date:
- Size: 64.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a54569ae64ae9713056a030d23fe25c34003447d944da497646e6edb2c3cc07b
|
|
| MD5 |
b2302c6c9d6c44ee31d3b5224c52b512
|
|
| BLAKE2b-256 |
303b6546ae22e4da1e28c3458e16abc22902b928d4d8cbbba8924e7d0d1b12b6
|
Provenance
The following attestation bundles were made for datasette_user_profiles-0.1.0a7-py3-none-any.whl:
Publisher:
publish.yml on datasette/datasette-user-profiles
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
datasette_user_profiles-0.1.0a7-py3-none-any.whl -
Subject digest:
a54569ae64ae9713056a030d23fe25c34003447d944da497646e6edb2c3cc07b - Sigstore transparency entry: 1830217716
- Sigstore integration time:
-
Permalink:
datasette/datasette-user-profiles@5533dcecdb8875cea702d5282a7c44c49b8fb5e7 -
Branch / Tag:
refs/tags/0.1.0a7 - Owner: https://github.com/datasette
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5533dcecdb8875cea702d5282a7c44c49b8fb5e7 -
Trigger Event:
release
-
Statement type: