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
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,15 @@ Eleven actions in three groups, plus one hidden.

### Configure

The four general settings — default locale, default phone region, the maintenance-window start hour, and whether new accounts are seeded with skeleton files — plus the Talk call-relay toggle.
The five general settings — default locale, default phone region, how long deleted files are kept, the maintenance-window start hour, and whether new accounts are seeded with skeleton files — plus the Talk call-relay toggle.

- **What it changes:** the first four are keys in `config.php`. **Relay Talk Calls Through Coturn** is a flag in `store.json`; through it the package's Coturn dependency and Nextcloud Talk's own STUN/TURN settings, which the `talk-turn` oneshot reconciles on the next start.
- **What it changes:** the first five are keys in `config.php`. **Relay Talk Calls Through Coturn** is a flag in `store.json`; through it the package's Coturn dependency and Nextcloud Talk's own STUN/TURN settings, which the `talk-turn` oneshot reconciles on the next start.
- **Cost:** seconds, then a restart.
- **Repeat safety:** idempotent; the form is pre-filled.

**Relaying is advertised only once Coturn has a public domain.** With the toggle on but Coturn lacking one, no relay is configured and nothing reports that as an error — Talk falls back to direct connections, which is what it would do anyway. The toggle also does nothing until the Talk app itself is installed from the Nextcloud app store; until then the oneshot logs that it is waiting and applies the setting on a later start.
**Delete Files in Trash** sets `trashbin_retention_obligation`. Nextcloud's default (`auto`) keeps a deleted file for at least 30 days and then removes it only as disk space is needed, so on a server with room to spare the trash grows without bound; the presets write `auto, <days>` to cap it, or `disabled` to keep everything. Only the maximum is offered — Nextcloud's `D1, D2` form also sets a guaranteed floor, which would hold files past the cap the user chose.

**Relaying is advertised only once Coturn has a public domain.** With the toggle on but Coturn lacking one, no relay is configured and nothing reports that as an error — Talk falls back to direct connections, which is what it would do anyway. The toggle also does nothing until the Talk app itself is installed from the Nextcloud app store **and enabled** — `occ` exposes an app's command namespace only while it is enabled, and a major Nextcloud upgrade disables any app without a compatible release. Until then the oneshot logs that it is waiting and applies the setting on a later start.

**Only the entries this package added are ever touched.** The `talk-turn` oneshot records what it applied and deletes exactly that before writing the new set, so Talk's default `stun.nextcloud.com:443` and anything an admin added by hand survive. Removing that default is a deliberate choice left to the admin — see Limitations.

Expand All @@ -161,7 +163,7 @@ Surfaces another StartOS service's files as a folder in Nextcloud Files, using N

- **Reset Admin Password** generates a new password for a chosen admin account and shows it once. Only while running; the account list is read live.
- **Disable Maintenance Mode** clears a stuck maintenance flag. Only while running. **Wait first** — brief maintenance mode after an update or a restart is normal, and this is for when it has lasted more than about fifteen minutes.
- **Disable Non-default Apps** turns off every app that is not part of Nextcloud's default set. It is the recovery for an app that has made the UI return an Internal Server Error. Only while running, and **stable apps must be re-enabled individually afterwards.**
- **Disable Non-default Apps** turns off every enabled app that Nextcloud does not ship, preserving the bundled set plus Calendar and Contacts. It is the recovery for an app that has made the UI return an Internal Server Error. Apps are disabled one at a time, and the result lists any that could not be — a fataling app is exactly what this action targets, so its own failure must not hide what did get disabled. Only while running, and **stable apps must be re-enabled individually afterwards.**
- **Scan Files** rebuilds the file-cache index, which is what makes files added outside Nextcloud — over WebDAV's back door, rsync, or an external-storage mount — appear with correct sizes and turn up in search.
- **Repair** runs Nextcloud's built-in repair routine against database inconsistencies, stale cache entries, and broken shares.

Expand Down
7 changes: 7 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ Current pin: `valkey/valkey:9-alpine` in `startos/manifest/index.ts` (`images.va

**Nextcloud** — bump the `NEXTCLOUD_VERSION` `ARG` default in `nextcloud.Dockerfile` to the new patch version (e.g. `32.0.9` → `32.0.10`). The `-apache` suffix is appended by the `FROM` line; don't include it in the ARG value.

A **major** bump (e.g. `33.0.8` → `34.0.3`) needs four more checks:

- Diff `core/shipped.json` between the two tags and add whatever the new major appends to `defaultEnabled` and `alwaysEnabled` to the `defaultApps` list in `startos/actions/maintenance/disableUnstableApps.ts`. `occ app:disable` exits non-zero on an `alwaysEnabled` app, and the action runs under `execFail`, so one missing id breaks the recovery action a locked-out user is told to run.
- Check `$OC_VersionCanBeUpgradedFrom` in the new tag's `version.php`. Nextcloud upgrades one major at a time, so every version the registry still lists must be within one major of the new image — `startos/init/bootstrapNextcloud.ts` refuses the rest and StartOS rolls the update back.
- Diff `config/config.sample.php` between the two tags against the keys `startos/fileModels/config.php.ts` models, and against the defaults the README and `instructions.md` quote.
- Re-check the new major's system requirements page for the supported PostgreSQL and PHP versions.

**Postgres** — change the `dockerTag` value in `images.postgres.source` in `startos/manifest/index.ts`. The pin tracks the `17` major; bumping across major versions (e.g. `17` → `18`) requires a data migration in `startos/versions/`.

**Valkey** — change the `dockerTag` value in `images.valkey.source` in `startos/manifest/index.ts`. The pin tracks the `9` major.
6 changes: 3 additions & 3 deletions instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ Point a Nextcloud desktop or mobile client (or any WebDAV client) at the **WebDA

### Actions

- **Configure** — set the default locale, default phone region, the UTC start hour of Nextcloud's nightly maintenance window for background jobs, a toggle to stop seeding new user accounts with Nextcloud's default skeleton files (sample documents, photos, README), and **Relay Talk Calls Through Coturn** (see below).
- **Configure** — set the default locale, default phone region, how long deleted files are kept before Nextcloud removes them for good (**Delete Files in Trash** — by default they are kept at least 30 days and then cleared only when disk space runs short, so trash can pile up on a server with room to spare), the UTC start hour of Nextcloud's nightly maintenance window for background jobs, a toggle to stop seeding new user accounts with Nextcloud's default skeleton files (sample documents, photos, README), and **Relay Talk Calls Through Coturn** (see below).
- **External Storage** — surface another StartOS service's storage as a folder in your Nextcloud **Files**, using Nextcloud's built-in External Storage app. The action lists a dropdown for each supported service **you have installed** (today just **File Browser** → a `/FileBrowser` folder). Each dropdown is **Not mounted** (off), **Available to all users**, or **Available to specific users** (which then lets you pick exactly which Nextcloud users see it). The folder is read-write, so you can **move files out of it into Nextcloud**. Nextcloud must be **running** to run this action (it reads your live user list). Files other services add to File Browser appear automatically when you open the folder — so File Browser acts as the shared hub: point any service that should be visible in Nextcloud at File Browser.
- **Reset Admin Password** — pick an admin user and generate a new random password. Use this if the admin password is lost or you want to rotate it.
- **Disable Maintenance Mode** (Maintenance group) — runs `occ maintenance:mode --off`. Brief maintenance mode after an update or restart is normal — wait at least 15 minutes before resorting to this. Nextcloud version updates now run as part of the StartOS update step and roll back cleanly if they fail, so you should rarely need this.
- **Disable Non-default Apps** (Maintenance group) — disables every non-default Nextcloud app. Use this if a third-party app has broken the UI with an Internal Server Error. Stable apps must then be re-enabled individually from the Nextcloud Apps page.
- **Disable Non-default Apps** (Maintenance group) — disables every enabled app that Nextcloud does not ship, keeping the bundled set plus Calendar and Contacts. Use this if a third-party app has broken the UI with an Internal Server Error. The result lists what was disabled, and separately anything that could not be, so a single stuck app does not stop the rest. Stable apps must then be re-enabled individually from the Nextcloud Apps page.
- **Scan Files** (Maintenance group) — queues a background rebuild of the file cache after files are synced into Nextcloud externally (rclone, rsync, SFTP, etc.). Run this if externally synced files appear stale, show wrong sizes, or are missing from search. On a large library the scan can take many minutes; progress shows as the **File Scan** health check on the service status page, and StartOS posts a notification when it finishes.
- **Repair** (Maintenance group) — queues a background run of Nextcloud's built-in repair routine, fixing database inconsistencies, stale cache entries, and broken shares. Run this if files appear missing, shares return errors, or after a crash or abrupt shutdown. Progress shows as the **Repair** health check on the service status page, and StartOS posts a notification when it finishes.
- **Download Machine Learning Models for Recognize** (App Commands group) — queues a background download of the ML models the Recognize app needs for object and face detection (~1-2 GB). Install the Recognize app from the Nextcloud App Store first. Progress shows on the service status page while the download is in flight, and StartOS posts a notification when it finishes.
Expand All @@ -50,7 +50,7 @@ Nextcloud Talk places calls directly between the two participants, which fails w

Two things have to be in place first:

1. **Install the Talk app** in Nextcloud, from **Apps** in the Nextcloud web UI.
1. **Install the Talk app** in Nextcloud, from **Apps** in the Nextcloud web UI. It also has to stay enabled — a Nextcloud major upgrade disables any app that has no compatible release yet, and relaying is not applied again until you update Talk and re-enable it.
2. **Install the separate Coturn service** from the StartOS marketplace, start it, and give it a public domain of its own, as its own instructions describe.

Then turn the toggle on. Until all three are done, calls still work wherever a direct connection is possible, and nothing reports the missing relay as an error.
Expand Down
2 changes: 1 addition & 1 deletion nextcloud.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NEXTCLOUD_VERSION=33.0.8
ARG NEXTCLOUD_VERSION=34.0.3
FROM nextcloud:${NEXTCLOUD_VERSION}-apache

RUN apt-get update \
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 33 additions & 21 deletions startos/actions/maintenance/disableUnstableApps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { i18n } from '../../i18n'
import { sdk } from '../../sdk'
import { nextcloudMount } from '../../utils'
import { nextcloudMount, readEnabledApps } from '../../utils'

export const disableUnstableApps = sdk.Action.withoutInput(
// id
Expand All @@ -23,6 +23,7 @@ export const disableUnstableApps = sdk.Action.withoutInput(
// the execution function
async ({ effects }) => {
const disabledApps: string[] = []
const failedApps: string[] = []
await sdk.SubContainer.withTemp(
effects,
{ imageId: 'nextcloud' },
Expand All @@ -33,6 +34,7 @@ export const disableUnstableApps = sdk.Action.withoutInput(
'activity',
'admin_audit',
'app_api',
'appstore',
'bruteforcesettings',
'calendar',
'circles',
Expand All @@ -48,6 +50,7 @@ export const disableUnstableApps = sdk.Action.withoutInput(
'files',
'files_downloadlimit',
'files_external',
'files_lock',
'files_pdfviewer',
'files_reminders',
'files_sharing',
Expand All @@ -59,6 +62,7 @@ export const disableUnstableApps = sdk.Action.withoutInput(
'nextcloud_announcements',
'notifications',
'oauth2',
'office',
'password_policy',
'photos',
'privacy',
Expand Down Expand Up @@ -87,32 +91,40 @@ export const disableUnstableApps = sdk.Action.withoutInput(
'workflowengine',
]

const res = await sub.execFail(
['php', 'occ', 'app:list', '--enabled', '--output=json'],
{ user: 'www-data' },
)

const parsed = JSON.parse(res.stdout as string) as {
enabled: Record<string, string>
// Sequential, and tolerating failure: `occ app:disable` runs the app's
// own uninstall repair steps, so a fataling app — the very thing this
// action exists to escape — can exit non-zero or outlast exec's 30 s
// timeout. That must cost only its own line in the report.
for (const app of Object.keys(await readEnabledApps(sub))) {
if (defaultApps.includes(app)) continue
const res = await sub.exec(['php', 'occ', 'app:disable', app], {
user: 'www-data',
})
if (res.exitCode === 0) {
disabledApps.push(app)
} else {
failedApps.push(app)
console.error(
`disable-unstable-apps: could not disable ${app}: ${res.stdout.toString()} ${res.stderr.toString()}`,
)
}
}

await Promise.all(
Object.keys(parsed.enabled).map((app) => {
if (!defaultApps.includes(app)) {
disabledApps.push(app)
return sub.execFail(['php', 'occ', 'app:disable', app], {
user: 'www-data',
})
}
}),
)
},
)

const list = (apps: string[]) =>
`<ul>${apps.map((app) => `<li>${app}</li>`).join('')}</ul>`

return {
version: '1',
title: i18n('Success'),
message: `${i18n('The following apps have been disabled:')} <ul>${disabledApps.map((app) => `<li>${app}</li>`).join('')}</ul>`,
title: failedApps.length ? i18n('Partially Successful') : i18n('Success'),
message:
(disabledApps.length
? `${i18n('The following apps have been disabled:')} ${list(disabledApps)}`
: i18n('No non-default apps were enabled.')) +
(failedApps.length
? `${i18n('These apps could not be disabled. The service logs say why:')} ${list(failedApps)}`
: ''),
result: null,
}
},
Expand Down
10 changes: 9 additions & 1 deletion startos/actions/setConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { configPhp } from '../fileModels/config.php'
import { storeJson } from '../fileModels/store.json'
import { i18n } from '../i18n'
import { sdk } from '../sdk'
import { locales, phoneRegions } from '../utils'
import { locales, phoneRegions, trashRetention } from '../utils'

const { InputSpec, Value } = sdk

Expand All @@ -23,6 +23,14 @@ export const inputSpec = InputSpec.of({
default: 'US',
values: phoneRegions,
}),
trashbin_retention_obligation: Value.select({
name: i18n('Delete Files in Trash'),
description: i18n(
"How long Nextcloud keeps a deleted file in each user's Deleted Files before removing it for good. By default files are kept for at least 30 days and then removed only as disk space is needed, so trash can grow without bound on a server with room to spare. Setting a limit removes them on schedule instead. Restoring a file is only possible while it is still in Deleted Files.",
),
default: 'auto',
values: trashRetention,
}),
maintenance_window_start: Value.number({
name: i18n('Maintenance Window Start Time'),
description: i18n(
Expand Down
5 changes: 4 additions & 1 deletion startos/fileModels/config.php.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FileHelper, z } from '@start9labs/start-sdk'
import { sdk } from '../sdk'
import { locales, phoneRegions } from '../utils'
import { locales, phoneRegions, trashRetention } from '../utils'

const UPDATER_SERVER_URL = 'http://updates.disabled.invalid/'

Expand All @@ -22,6 +22,9 @@ const shape = z.object({
default_phone_region: z
.enum(Object.keys(phoneRegions) as [string, ...string[]])
.catch('US'),
trashbin_retention_obligation: z
.enum(Object.keys(trashRetention) as [string, ...string[]])
.catch('auto'),
maintenance_window_start: z.number().int().min(0).catch(24),
overwriteprotocol: z.null().optional().catch(undefined),
'memcache.local': z
Expand Down
9 changes: 9 additions & 0 deletions startos/i18n/dictionaries/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,15 @@ const dict = {

// versions/from035x.ts: 0.3.5x migration progress
'Updating file permissions': 134,

// disableUnstableApps.ts: per-app outcome report
'Partially Successful': 137,
'No non-default apps were enabled.': 138,
'These apps could not be disabled. The service logs say why:': 139,

// setConfig.ts: trash retention
'Delete Files in Trash': 140,
"How long Nextcloud keeps a deleted file in each user's Deleted Files before removing it for good. By default files are kept for at least 30 days and then removed only as disk space is needed, so trash can grow without bound on a server with room to spare. Setting a limit removes them on schedule instead. Restoring a file is only possible while it is still in Deleted Files.": 141,
} as const

/**
Expand Down
Loading