Before you start
1 module ยท no track requiredInstall and configure a Nextcloud development environment using Docker or GitHub Codespaces.
Understand the structure of a Nextcloud app โ directories, the manifest, routing, and the bootstrap class.
Controllers, the OCP namespace, dependency injection in practice, and returning your first responses.
Create your first migration, define entities, and read/write data with QBMapper.
Set up Vite, install npm dependencies, wire the PHP template to a Vue entry point, and verify the build pipeline works.
Build the Pinboard UI in three stages โ fetch and display pins, add loading and empty states, then wire up a form to create new ones.
Get the current user, scope all queries to them, and understand basic capability checks.
Version your app, sign it, and submit it to the Nextcloud app store.
PHP App Track โ Intermediate
coming soonWhy the OCP abstraction layer exists, what's public API and what isn't, and how to navigate deprecations across versions.
Introduce boards as an organisational layer โ a new entity, a migration adding nullable board_id to pins, and the uncategorised pseudo-board pattern. Add and remove pins from boards, list pins by board.
Split the monolithic App.vue into a proper component tree โ BoardList, PinList, PinCard. Introduce Pinia for shared state so boards and pins are accessible across components without prop drilling. Understand when to split a component and how to design the store around the app's data model.
Nextcloud's sharing model applied to Pinboard boards and individual pins. User shares, group shares, link shares, password-protected shares, and email invites. The permission bitmask โ read, create, update, delete, share โ and how to check and enforce each. Public board pages with #[PublicPage] and share tokens.
NcAppNavigation for the board sidebar, NcSelect for user and group pickers in the share dialog, NcActionButton for per-pin actions, NcDialog for confirmations. Keyboard navigation and focus management. Accessibility in practice โ ARIA live regions for dynamic pin lists, focus trapping in modals.
INotificationManager and IActivityManager driven by real Pinboard events โ alice shared a board with you, bob added a pin to Research, a board you follow has new pins. Email notifications via IMailer for share invites.
Build a proper versioned REST API for boards and pins. OCS vs plain JSON, authentication strategies, and testing with curl and Bruno. The Initial State mechanism โ provideInitialState() in the controller paired with loadState() in the frontend โ eliminates the initial API round-trip by injecting server data directly into the page on load.
CSRF, input validation, output escaping, SQL injection prevention, and auditing board and share permission checks against OWASP Top 10.
IJob, TimedJob, QueuedJob โ when to use each, registration, failure handling, and progress reporting. Exercise: a digest job summarising new pins added to shared boards.
Implement ISearchProvider for pins and boards โ indexing strategy, what to surface in unified search, and scoping results to boards the user can access.
ISettings for personal and admin scopes, IConfig for instance-wide configuration. Exercise: admin toggle to disable public board sharing instance-wide.
Lazy-loading Vue components with defineAsyncComponent for heavy views like the share dialog. Optimistic updates with rollback when the server rejects a change. Pagination and infinite scroll for large pin lists. Debouncing search input. Skeleton loading states instead of spinners.
N+1 query detection and caching with ICache and IMemcache applied to board and pin queries.
PHPUnit setup, unit vs integration tests, mocking Nextcloud services including the share manager and notification manager. Frontend testing with Jest.
PHP App Track โ Advanced
coming soonThe full bootstrap sequence from HTTP request to controller call. DI container internals โ how constructor parameters are resolved, lazy proxies, named parameters like $userId. OC_Hook legacy system vs IEventDispatcher. How Nextcloud's classloader maps namespaces to directories.
IEventDispatcher โ publishing and subscribing to events. Designing Pinboard event classes for board creation, pin addition, and share changes. Decoupling notifications, activity, and search indexing from controller logic so they listen independently. Avoiding circular dependencies in event handlers.
The OCM protocol, implementing ICloudFederationProvider for Pinboard boards. Sharing a board with a user on a remote Nextcloud instance, remote user identity and trust boundaries. Handling incoming share notifications and syncing pin activity across instances.
Server-Sent Events (SSE) and long-polling to push live updates to shared boards โ new pins appear without refresh. Dashboard widget showing recent activity across all boards. URL preview data fetching as a background job triggered on pin creation, with Open Graph metadata stored and surfaced in the UI. The limits of Nextcloud's notification polling mechanism.
The storage abstraction stack (IStorage, Jail, Wrapper). File attachments on pins โ storing files in the user's Nextcloud rather than as blobs. Mount points and file IDs across mounts, why you can't trust paths alone. Chunked upload handling. WebDAV from the frontend for direct file operations.
ICacheFactory in depth โ local, distributed, and Redis-backed caches and when to use each. Distributed locking with ILockingProvider for concurrent board edits (two users adding a pin simultaneously). Cache invalidation strategies for board membership and share ACLs. IMemcache vs ICache โ the distinction that matters under load.
How Nextcloud handles horizontal scaling โ stateless vs stateful components. The filecache table at scale and why large queries against it are dangerous. Object storage (S3/Swift) implications for file attachments on pins. Designing board and pin queries to be safe at 10M+ rows.
Content Security Policy builder, ISecureRandom vs ICrypto โ when to use each. Capability-based access control patterns. Rate limiting on share creation and public board endpoints with IRateLimiter. Audit logging for share events. Supply chain โ evaluating JS dependencies, composer security advisories, REUSE compliance.
Reading and navigating the Nextcloud server codebase. The backport process and stable branch rules. Writing a compelling RFC or GitHub discussion. Working with the public API committee โ what gets into OCP and what doesn't. Running the full Nextcloud test suite locally.
GitHub Actions for Nextcloud apps โ the standard workflow templates. Automated compatibility testing across Nextcloud versions. Psalm with Nextcloud stubs, PHP-CS-Fixer, ESLint, and Stylelint. App store release automation. REUSE compliance in CI.
When to split Pinboard into multiple apps โ board app vs pin app โ and the library app pattern for shared code. Sidecar processes and IPC. Zero-downtime migrations for apps with active installs. Migrating from autoincrement to SnowflakeAwareEntity primary keys on a table with existing data. The decision framework for when an app should become an ExApp. Closes with a self-directed extensions section pointing to bonus module candidates.
Install Nextcloud, AppAPI, and the ExApp daemon. Understand the Docker-based deployment model and how an ExApp registers with Nextcloud. First occ app_api:app:register cycle.
The ExApp manifest, registration with AppAPI, nc_py_api basics. How Nextcloud calls your service โ the signing protocol, what headers arrive, and what you must verify before trusting a request. The ExApp lifecycle vs a PHP app lifecycle.
Request handlers with FastAPI, nc_py_api for calling Nextcloud APIs, and returning JSON responses from your first Pinboard endpoints. How requests reach your Python service and how to return typed responses โ the ExApp equivalent of PHP controllers.
Nextcloud's KV store via nc_py_api for lightweight config, your own SQLite/Postgres sidecar for structured pin data, and when to use each. Define the pins table, write basic CRUD, scope all queries to the current user.
Set up Vite and Vue for an ExApp โ serving static assets from the Python service, wiring the registered top-level entry to a Vue entry point. The ExApp frontend model differs from PHP apps: assets are served by your service, not by Nextcloud.
Build the Pinboard UI in three stages โ fetch and display pins, add loading and empty states, then wire up a form to create new ones. @nextcloud/axios for CSRF-safe calls back to your own Python API. Same @nextcloud/vue components as the PHP track.
Verifying the current user via AppAPI โ what identity claims arrive in the request, what you can trust, and what you must verify. Scoping all pin queries to the current user. The difference between nc_py_api user context and a PHP IUserSession.
Building the Docker image, the ExApp store manifest, signing, and registering your app on the Nextcloud app store. The differences from PHP app packaging โ Docker image vs signed zip, AppAPI registration vs plain app enable.
ExApp Track โ Intermediate
coming soonThe AppAPI auth handshake in depth โ verifying request signatures, never trusting unverified callbacks, replay attack prevention. Auditing the beginner pin and board endpoints against OWASP Top 10. User identity trust boundaries.
Introduce boards as an organisational layer โ a new sidecar DB entity, nullable board_id on pins, and the uncategorised pseudo-board pattern. Add and remove pins from boards, list pins by board. The ExApp equivalent of the PHP Boards module โ same concept, your sidecar DB means no Nextcloud migration required.
Split the monolithic App.vue into a proper component tree โ BoardList, PinList, PinCard. Introduce Pinia for shared state. Understand when to split a component and how to design the store around boards and pins.
Hooking into Nextcloud's share manager from an ExApp via OCS. Sharing boards and individual pins with users, groups, via link, password-protected, and email invite. The permission bitmask โ read, create, update, delete, share โ checked and enforced in your Python service. Public board pages via nc_py_api.
NcAppNavigation for the board sidebar, NcSelect for user and group pickers in the share dialog, NcActionButton for per-pin actions, NcDialog for confirmations. Keyboard navigation, focus management, and accessibility in practice.
Sending notifications to Nextcloud users from your Python service via nc_py_api โ alice shared a board with you, bob added a pin to Research. IActivityManager integration. Email notifications for share invites via the Nextcloud mailer OCS endpoint.
Exposing your own versioned API endpoints from the ExApp for boards and pins. OCS response format, API versioning, authentication for external callers. Registering endpoints with AppAPI so Nextcloud proxies them correctly. Note: unlike PHP apps, ExApps do not use the IInitialState mechanism โ initial data is fetched via a standard API call on component mount, consistent with the ExApp stateless service model.
Reading and writing user files via WebDAV and nc_py_api. File attachments on pins โ storing files in the user's Nextcloud rather than in the sidecar DB. Understanding file IDs across mounts and handling permissions correctly.
Async processing within your Python service using asyncio or task queues. Webhooks from Nextcloud triggering async work. Progress reporting back to the UI. Exercise: async URL preview fetching triggered on pin creation.
Registering a search provider from an ExApp via nc_py_api. Indexing boards and pins in unified search, scoping results to boards the requesting user can access. Handling search from an external service โ latency considerations.
Serving admin and personal settings panels from your ExApp, reading and writing instance configuration via nc_py_api. Exercise: admin toggle to disable public board sharing instance-wide.
Lazy-loading Vue components with defineAsyncComponent. Optimistic updates with rollback when the Python service rejects a change. Pagination and infinite scroll for large pin lists. Debouncing search input. Skeleton loading states.
Connection pooling to Nextcloud OCS APIs, caching strategies for share ACL lookups and board membership, avoiding redundant API calls with local sidecar DB as cache. Profiling the Python service. N+1 detection on board+pin queries.
Testing ExApp business logic independently of Nextcloud โ unit tests for board and pin logic, mock nc_py_api. Integration testing against a live Nextcloud instance. Testing share ACL enforcement. Frontend testing with Jest.
ExApp Track โ Advanced
coming soonHow the daemon model works under the hood โ the full signing protocol, request routing, failure modes, and recovery. How registration flows from occ through the daemon to your service. Lazy ExApp loading and capability negotiation.
Implementing TaskProcessingProvider, SpeechToText, TextToImage, and ContextChat from an ExApp. AI-powered pin tagging โ automatically categorise new pins into boards using a language model. The async task processing lifecycle.
OCM from a Python ExApp โ implementing the federation protocol to share Pinboard boards across Nextcloud instances. Cross-instance data exchange via nc_py_api, remote user identity trust boundaries, incoming share handling.
Server-Sent Events from a Python service to push live board updates โ new pins appear without refresh. Dashboard widget showing recent board activity. Progress reporting for AI task processing. The interaction between SSE, the Pinia store, and optimistic updates.
Running the ExApp as a horizontally scaled service โ stateless design, load balancer considerations, sidecar DB becomes distributed. Redis for shared cache and session state across instances. Object storage for file attachments. When the sidecar DB model breaks down.
Container image supply chain โ base image selection, dependency scanning, SBOM generation. Secrets management โ never bake credentials into images. CSP implications of an external service serving assets. Rate limiting on share creation and public board endpoints. Runtime security and least-privilege containerisation.
Caching OCS API responses to avoid redundant Nextcloud calls โ share ACL lookups, user group membership, board metadata. Redis as a shared cache across scaled instances. Cache invalidation via Nextcloud webhook events. When to cache and when to always re-fetch.
Receiving Nextcloud events via webhook callbacks โ file events, user events, share events. Designing the ExApp as an event-driven service: decoupling board notifications, search indexing, and AI tagging into independent event handlers. Webhook reliability, retries, and idempotency.
Docker image builds for multiple architectures (amd64, arm64). Automated AppAPI registration testing against a real Nextcloud instance in CI. App store release automation. Container image signing and attestation. SBOM generation in the pipeline.
Contributing to AppAPI itself โ new provider interfaces, capability additions, nc_py_api improvements, and the AppAPI release process. Navigating the AppAPI codebase, writing good issues, and the PR review process.
When to split an ExApp into multiple services. The sidecar service pattern for heavy computation (AI inference, image processing). Migrating from SQLite to Postgres in a sidecar DB with live users. The decision framework for when an ExApp should become a PHP app or vice versa. Closes with a self-directed extensions section.