Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
scripts/docker-entrypoint.sh text eol=lf
docs/examples/operations/*.sh text eol=lf
**/deft.app.lock.json text eol=lf
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Check public documentation and tutorial manifest
run: pnpm docs:check
- name: Verify bundled module provenance
run: pnpm module:verify
- name: Test App Kit authoring contracts
Expand Down
3 changes: 2 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ Fresh installs use `init` (`pnpm db:push-full` plus platform seed). Supported ve

## Apps and Modules

- Modules define domain records, relationships, and Deft-rendered native views.
- Modules define domain records, relationships, and Deft-rendered native views. Standalone manifests install through Settings → Modules without the Apps feature flags.
- Module v1 data is shared with owners, admins, and members; it has no private rows or fields. [Choose between a Module and an App](docs/modules-and-apps.md).
- Declarative internal Apps package Modules for review and installation and are an opt-in alpha capability.
- Connected Apps and bounded daily actions are included in preview.15. They are experimental, disabled by default, and require the flags and review flow in the [operator guide](docs/app-run-operations.md).
- The current App protocols do not provide arbitrary custom UI, public portals, general external runtimes, or synchronization.
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ Use `init` only with a fresh database. For custom ports, public URLs, image veri

[Architecture](#architecture) · [Current limitations](docs/current-limitations.md) · [Licensing](#license)

## Try one workflow

- [Set up your first workspace](docs/tutorials/first-workspace.md)
- [Ask Defty for a task and approve it](docs/tutorials/first-agent-action.md)
- [Connect a personal AI client](docs/tutorials/first-mcp-connection.md)
- [Build a small vendor Module](docs/tutorials/first-module.md)

## The core loop

1. **Capture the discussion.** Save useful decisions and references in Knowledge with links back to their source conversation.
Expand All @@ -78,7 +85,7 @@ Optional video chapters: [Knowledge capture — 1:09](https://youtu.be/7z9EH4c9k
### A workspace people can use normally

- Real-time chat with spaces, DMs, threads, mentions, reactions, files, presence, and rich text
- Task management with Board, Table, Timeline, Calendar, Pipeline, and personal views
- Task management with Board, Table, Timeline, Calendar, and personal views
- Notes, company knowledge, channel memory, decisions, references, and knowledge graph views
- Native calendar events plus read-only ICS subscriptions
- Dashboard, inbox, notifications, people, teams, roles, and profile management
Expand All @@ -97,7 +104,7 @@ Deft still works as a normal workspace without an AI provider key. Chat, tasks,

### An extensible workspace through Modules and Apps

Modules add domain records, relationships, and native views. Apps package supported workspace extensions for operator review and installation. Declarative internal Apps, connected Apps, and bounded scheduled actions are opt-in alpha capabilities and remain disabled by default. Arbitrary custom UI and public portals are planned rather than part of the current contract.
Modules add domain records, relationships, and native views. Apps package Module resources and may request supported connected actions. They use different manifests and installation flows; start with [Modules and Apps](docs/modules-and-apps.md). Declarative internal Apps, connected Apps, and bounded scheduled actions are opt-in alpha capabilities and remain disabled by default. Arbitrary custom UI and public portals are planned rather than part of the current contract.

The bundled **Contacts** module is the first example of this model. The goal is not to turn Deft's core into every application a company might need, but to let new capabilities live on the same shared substrate instead of becoming another disconnected system.

Expand All @@ -111,7 +118,7 @@ Chat is both a human communication surface and part of the workspace record. Thr

### Tasks turn context into accountable work

Projects support Board, Table, Timeline, Calendar, and Pipeline views, plus dependencies, subtasks, recurrence, comments, activity diffs, bulk actions, and agent-created drafts.
Projects support Board, Table, Timeline, and Calendar views, plus dependencies, subtasks, recurrence, comments, activity diffs, bulk actions, and agent-created drafts.

![Deft task table](docs/assets/repository/tasks-table.png)

Expand Down
4 changes: 4 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,7 @@ Historical tags through `v0.2.0-preview.4` retain BSL 1.1 as shipped.
Do not rewrite those tags. GitHub's source archive plus the repository's
build and installation scripts are the Corresponding Source offered with
the official image.

## Documentation gate

Complete [the documentation release checks](docs/documentation-maintenance.md#before-a-release) before publication. Keep the website and README on the same recommended installation path, and record install, restore, upgrade, MCP, and Module evidence for the release.
14 changes: 14 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,22 @@
- [Security](../SECURITY.md)
- [Contributing](../CONTRIBUTING.md)

## Learn by doing

- [Your first workspace](tutorials/first-workspace.md)
- [Your first approved agent action](tutorials/first-agent-action.md)
- [Connect your first AI client](tutorials/first-mcp-connection.md)
- [Build your first Module](tutorials/first-module.md)
- [Create a task through MCP](tutorials/mcp-task-example.md)

The tutorials target `v0.3.0-preview.15`. Their examples live in
[`docs/examples/`](examples/README.md). Use the website for navigation and search,
and the matching release's source for operational contracts.

## Operator and integration guides

- [Modules and Apps: choose a starting point](modules-and-apps.md)

- [Connected App author guide](connected-app-author-guide.md)
- [Historical Hermes compatibility guide](../integrations/hermes/deft-platform/README.md) (use only with a release that explicitly certifies it; excluded from the preview.15 core release)
- [Legacy Hermes Agent Channel bridge rollback](hermes-agent-channel-service.md)
Expand Down
2 changes: 2 additions & 0 deletions docs/connected-app-author-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

> **Experimental in preview.15:** this connected-App and bounded-automation flow is included in the published `v0.3.0-preview.15` core release. It is experimental and disabled by default.

For a standalone tracker, use the [Module tutorial](tutorials/first-module.md). A `deft.module.json` installs through Settings → Modules; this guide packages resources in a separate `deft.app.json` and uses Settings → Apps. [Compare the two paths](modules-and-apps.md).

This guide shows how to build a connected App, check that it matches the host,
and submit it for workspace review. It supports App Protocol v1 connected Apps
and the bounded scheduling requests in Protocol v2. The workspace operator
Expand Down
2 changes: 2 additions & 0 deletions docs/current-limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Deft is an alpha. It is suitable for technical evaluation, internal use, and con

## Apps and Modules

[Modules and Apps](modules-and-apps.md) use different packaging and installation paths. Standalone Modules do not require the experimental Apps flags. Module v1 records are organization-wide for owners, admins, and members; guests are denied, and private rows, collections, and fields are not supported.

- Apps are disabled by default. The API requires `DEFT_APPS_ENABLED=true`, and the web build requires `NEXT_PUBLIC_FEATURE_APPS=true`.
- Connected App execution and bounded automations add further flags and operator review. They are included in preview.15, experimental, and disabled by default.
- App Protocol v0 provides declarative internal records and native views. Current protocols do not provide arbitrary custom UI, public portals, general external runtimes, or synchronization.
Expand Down
36 changes: 36 additions & 0 deletions docs/documentation-maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Keeping documentation current

## Ownership

Keep deployment contracts, schemas, runnable examples, and tutorial source in this repository. The website presents those instructions with search and navigation. Historical plans and audits stay under `docs/superpowers/`; they are not setup guides.

The tutorial Markdown in `docs/tutorials/` is canonical. The website's `scripts/sync-product-docs.mjs` copies it into the documentation layout and copies the example downloads. Run the script with a checkout containing the reviewed documentation changes, then build and check the website. Do not maintain two independent tutorial drafts.

## Writing

- Lead with what the reader will accomplish.
- Name the starting state, required role, and relevant release.
- Use actual UI labels and commands from that release.
- Put a short expected result after each important action.
- Explain the likely failure next to the step it affects.
- Prefer one example over a long feature list. Link to reference details.
- Label illustrative output and demo screenshots. Do not imply they came from the reader's workspace.

## Before a release

1. Check the image tag and digest, downloaded asset names, and required secrets.
2. Follow the recommended fresh-install path in a disposable deployment.
3. Create a task and attachment. Back up, restore into a separate project, and verify both.
4. Rehearse the supported upgrade from its declared baseline. Record the previous and target image digests.
5. Check personal MCP scopes, an authenticated read, a write, and a retry with the same idempotency key.
6. Validate the example Module, install it, create its records, and test its upgrade.
7. Update changed limits, flags, client compatibility, and runtime certification statements.
8. Sync the website tutorials, build it, check links, and inspect desktop and mobile navigation.

Run `pnpm docs:check` for local links, the Module example, and operational scripts with a simulated Docker command. The tests cover restart behavior, failure handling, legacy uploads, checksums, target collisions, and successful restore commands. They require Bash and do not replace a real install or restore test.

In the website checkout, run `pnpm docs:sync --check /path/to/Deft` to compare against the reviewed source. The normal website build also checks synced-file integrity, release consistency, search behavior, rendered command/download parity, and local links. Synchronization refuses a tutorial that targets a different release from the site configuration.

## Record the evidence

For each workflow, record the release/commit, environment, command or user actions, result, and remaining limitation. Say “targets this release” when only source was checked. Reserve “verified” for an actual recorded run. Update the website release badge only after the release exists and its instructions are reviewed.
17 changes: 17 additions & 0 deletions docs/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Documentation examples

These examples target `v0.3.0-preview.15`. Use fictional data in a disposable workspace.

- `vendor-pilot/deft.module.json`: a two-collection Module for the [Module tutorial](../tutorials/first-module.md).
- `operations/backup-release.sh`: a backup example for a release-image deployment using Docker named volumes. It also saves legacy container uploads when present. Run `bash backup-release.sh` from the release folder, or add `--leave-stopped` before an upgrade.
- `operations/restore-release.sh`: a restore rehearsal in a new Compose project. Export `RECOVERY` (the absolute recovery-folder path) and `DEFT_RESTORE_IMAGE` (the saved immutable image digest) before running it. It refuses an existing target project or directory. Optional `RESTORE_PROJECT` and `RESTORE_DIR` choose other names; defaults are `deft-docs-restore` and `deft-restore`.

The Bash examples are for Linux. On Windows, use WSL with Docker integration; Git Bash needs its Docker path-conversion behavior accounted for. Rehearse on an isolated host because restored jobs and configured integrations can resume with the app.

Before upgrading an older deployment, copy captured legacy uploads into the persistent volume as described in [self-hosting](../self-hosting.md#upgrading). Restoring an older image also requires its original upload path and any source-mounted files; the restore example targets preview.15.

Validate the manifest from the repository root:

```bash
pnpm module:check docs/examples/vendor-pilot
```
48 changes: 48 additions & 0 deletions docs/examples/operations/backup-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
umask 077
leave_stopped=false
case "${1:-}" in
'') ;;
--leave-stopped) leave_stopped=true; shift ;;
--help) echo 'Usage: bash backup-release.sh [--leave-stopped]'; exit 0 ;;
*) echo 'Usage: bash backup-release.sh [--leave-stopped]' >&2; exit 1 ;;
esac
test "$#" -eq 0 || { echo 'Unexpected arguments.' >&2; exit 1; }
for tool in docker gzip sha256sum find sort xargs; do
command -v "$tool" >/dev/null || { echo "Missing $tool. Run this example in Linux or WSL with Docker integration." >&2; exit 1; }
done
dc() { docker compose -f docker-compose.yml -f compose.prod.yml -f compose.release.yml "$@"; }
mkdir -p "$PWD/backups"
RECOVERY="$(mktemp -d "$PWD/backups/deft-recovery-$(date -u +%Y%m%dT%H%M%SZ)-XXXXXX")"
container_id="$(dc ps -q deft)"
test -n "$container_id" || { echo 'Start from the running deployment.'; exit 1; }
uploads_volume="$(docker inspect "$container_id" --format '{{range .Mounts}}{{if eq .Destination "/app/uploads"}}{{.Name}}{{end}}{{end}}')"
test -n "$uploads_volume" || { echo 'Use the backup procedure for your storage mount.'; exit 1; }
docker volume inspect "$uploads_volume" >/dev/null
legacy_uploads="$(docker exec "$container_id" sh -c 'if [ -d /app/apps/api/uploads ]; then printf present; fi')"
dc stop deft
dc exec -T postgres pg_dump -U postgres --clean --if-exists --no-owner --no-privileges deft \
| gzip -9 > "$RECOVERY/database.sql.gz"
docker run --rm -v "$uploads_volume:/source:ro" -v "$RECOVERY:/backup" alpine:3.22 \
tar -C /source -czf /backup/uploads.tar.gz .
if [ "$legacy_uploads" = present ]; then
mkdir "$RECOVERY/legacy-container-uploads"
docker cp "$container_id:/app/apps/api/uploads/." "$RECOVERY/legacy-container-uploads/"
fi
docker inspect "$container_id" --format '{{.Image}}' > "$RECOVERY/running-image-id.txt"
docker image inspect "$(cat "$RECOVERY/running-image-id.txt")" --format '{{json .RepoDigests}}' \
> "$RECOVERY/running-image-repo-digests.json"
cp .env docker-compose.yml compose.prod.yml compose.release.yml "$RECOVERY/"
for file in release-manifest.json SHA256SUMS; do
if [ -f "$file" ]; then cp "$file" "$RECOVERY/release-$file"; fi
done
(cd "$RECOVERY" && find . -type f ! -name SHA256SUMS -print0 \
| sort -z | xargs -0 sha256sum > SHA256SUMS)
printf 'Recovery folder: %s\n' "$RECOVERY"
if "$leave_stopped"; then
echo 'Backup complete. Deft remains stopped for the upgrade.'
else
dc start deft
echo 'Backup complete. Deft restarted.'
fi
63 changes: 63 additions & 0 deletions docs/examples/operations/restore-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -euo pipefail
umask 077
for tool in docker gunzip sha256sum; do
command -v "$tool" >/dev/null || { echo "Missing $tool. Run this example in Linux or WSL with Docker integration." >&2; exit 1; }
done
: "${RECOVERY:?Set RECOVERY to the absolute path of your recovery folder}"
: "${DEFT_RESTORE_IMAGE:?Set DEFT_RESTORE_IMAGE to the digest saved in running-image-repo-digests.json}"
[[ "$RECOVERY" = /* ]] || { echo 'RECOVERY must be an absolute path.'; exit 1; }
[[ "$DEFT_RESTORE_IMAGE" =~ ^ghcr\.io/maneek21/deft@sha256:[a-f0-9]{64}$ ]] || { echo 'Use the saved Deft image digest.'; exit 1; }
(cd "$RECOVERY" && sha256sum -c SHA256SUMS)
RESTORE_PROJECT="${RESTORE_PROJECT:-deft-docs-restore}"
RESTORE_DIR="${RESTORE_DIR:-deft-restore}"
[[ "$RESTORE_PROJECT" =~ ^[a-z0-9][a-z0-9_-]*$ ]] || { echo 'RESTORE_PROJECT must use lowercase letters, digits, hyphens, or underscores.'; exit 1; }
test ! -e "$RESTORE_DIR" || { echo 'RESTORE_DIR already exists. Choose a new directory.'; exit 1; }
export COMPOSE_PROJECT_NAME="$RESTORE_PROJECT"
containers="$(docker ps -aq --filter "label=com.docker.compose.project=$COMPOSE_PROJECT_NAME")"
volumes="$(docker volume ls -q --filter "name=^${COMPOSE_PROJECT_NAME}_(pgdata|uploads)$")"
test -z "$containers$volumes" || { echo 'This restore project already has containers or volumes. Set RESTORE_PROJECT to an unused name.'; exit 1; }
mkdir "$RESTORE_DIR"
cd "$RESTORE_DIR"
cp "$RECOVERY/.env" "$RECOVERY/docker-compose.yml" "$RECOVERY/compose.prod.yml" "$RECOVERY/compose.release.yml" .
export DEFT_IMAGE="$DEFT_RESTORE_IMAGE"
export DEFT_BIND_HOST=127.0.0.1
export DEFT_WEB_PORT=127.0.0.1:3400
export DEFT_API_PORT=127.0.0.1:3401
export DEFT_POSTGRES_PORT=55432
export NEXT_PUBLIC_APP_URL=http://localhost:3400
export NEXT_PUBLIC_API_URL=http://localhost:3401
export NEXT_PUBLIC_WS_URL=http://localhost:3401
# Keep the rehearsal settings when this shell exits. Compose uses the last value.
cat >> .env <<EOF

# Restore rehearsal overrides
COMPOSE_PROJECT_NAME=$COMPOSE_PROJECT_NAME
DEFT_IMAGE=$DEFT_IMAGE
DEFT_BIND_HOST=$DEFT_BIND_HOST
DEFT_WEB_PORT=$DEFT_WEB_PORT
DEFT_API_PORT=$DEFT_API_PORT
DEFT_POSTGRES_PORT=$DEFT_POSTGRES_PORT
NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
NEXT_PUBLIC_WS_URL=$NEXT_PUBLIC_WS_URL
EOF
dc() { docker compose -f docker-compose.yml -f compose.prod.yml -f compose.release.yml "$@"; }
dc config --quiet
dc pull
dc up -d postgres
for attempt in {1..30}; do
if dc exec -T postgres pg_isready -U postgres -d deft; then break; fi
sleep 2
done
dc exec -T postgres pg_isready -U postgres -d deft
gunzip -c "$RECOVERY/database.sql.gz" | dc exec -T postgres psql -v ON_ERROR_STOP=1 -U postgres deft
docker run --rm -v "${COMPOSE_PROJECT_NAME}_uploads:/target" -v "$RECOVERY:/backup:ro" alpine:3.22 \
tar -C /target -xzf /backup/uploads.tar.gz
if [ -d "$RECOVERY/legacy-container-uploads" ]; then
docker run --rm -v "${COMPOSE_PROJECT_NAME}_uploads:/target" \
-v "$RECOVERY/legacy-container-uploads:/legacy:ro" alpine:3.22 cp -a /legacy/. /target/
fi
dc up -d deft
dc run --rm doctor
dc run --rm smoke
Loading