{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "pages": { "type": "array", "required": [ "name", "columns" ], "items": { "$ref": "#/definitions/page-item" } }, "theme": { "oneOf": [ { "$ref": "#/definitions/theme-item" }, { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/theme-item" }, { "$ref": "#/definitions/include" } ] } } ] }, "document": { "oneOf": [ { "$ref": "#/definitions/document-item" }, { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/document-item" }, { "$ref": "#/definitions/include" } ] } } ] }, "auth": { "oneOf": [ { "$ref": "#/definitions/auth-item" }, { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/auth-item" }, { "$ref": "#/definitions/include" } ] } } ] }, "server": { "oneOf": [ { "$ref": "#/definitions/server-item" }, { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/server-item" }, { "$ref": "#/definitions/include" } ] } } ] }, "branding": { "oneOf": [ { "$ref": "#/definitions/branding-item" }, { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/branding-item" }, { "$ref": "#/definitions/include" } ] } } ] } }, "definitions": { "include": { "type": "object", "required": [ "$include" ], "properties": { "$include": { "type": "string" } }, "additionalProperties": false }, "widget-base": { "type": "object", "required": [ "type" ], "properties": { "type": { "enum": [ "split-column", "extension", "docker-containers", "iframe", "twitch-channels", "bookmarks", "change-detection", "twitch-top-games", "server-stats", "repository", "lobsters", "dns-stats", "monitor", "releases", "html", "custom-api", "videos", "calendar", "reddit", "clock", "markets", "weather", "search", "rss", "to-do", "group", "hacker-news" ], "description": "Used to specify the widget." }, "title": { "type": "string", "description": "The title of the widget. If left blank it will be defined by the widget." }, "title-url": { "type": "string", "description": "The URL to go to when clicking on the widget's title. If left blank it will be defined by the widget (if available)." }, "cache": { "type": "string", "pattern": "^\\d+[smhd]$", "description": "How long to keep the fetched data in memory. The value is a string and must be a number followed by one of s, m, h, d.", "examples": [ "1h" ], "errorMessage": "Must be a valid time duration." }, "css-class": { "type": "string", "description": "Set custom CSS classes for the specific widget instance." }, "hide-header": { "type": "boolean", "description": "Hides the widget header when set to true. Not available for group widgets." } } }, "split-column-widget": { "oneOf": [ { "required": [ "widgets" ], "properties": { "max-columns": { "type": "integer", "minimum": 2, "description": "The maximum number of columns to split the widgets into." }, "widgets": { "type": "array", "minItems": 2, "description": "A list of widgets in the same format as on a page column. Accepts any widget apart from a split column or group widget.", "items": { "$ref": "#/definitions/widget-item" } }, "type": { "enum": [ "split-column" ] } } }, { "$ref": "#/definitions/include" } ] }, "extension-widget": { "oneOf": [ { "required": [ "url" ], "properties": { "url": { "type": "string", "description": "The URL of the extension. Note that the query gets stripped from this URL and the one defined by parameters gets used instead." }, "fallback-content-type": { "enum": [ "html" ], "description": "Optionally specify the fallback content type of the extension if the URL does not return a valid Widget-Content-Type header. Currently the only supported value for this property is html." }, "allow-potentially-dangerous-html": { "type": "boolean", "description": "Whether to allow the extension to display HTML. Must be enabled for html to render. Ensure you are aware the extension url you are using is safe." }, "parameters": { "type": "object", "additionalProperties": true, "examples": [ "key: value" ], "description": "A list of keys and values that will be sent to the extension as query paramters." }, "type": { "enum": [ "extension" ] } } }, { "$ref": "#/definitions/include" } ] }, "docker-containers-widget": { "oneOf": [ { "properties": { "hide-by-default": { "type": "boolean", "description": "Whether to hide the containers by default. If set to true you'll have to manually add a glance.hide: false label to each container you want to display. By default all containers will be shown and if you want to hide a specific container you can add a glance.hide: true label." }, "format-container-names": { "type": "boolean", "description": "When set to true, automatically converts container names such as container_name_1 into Container Name 1." }, "sock-path": { "type": "string", "description": "The path or URI to the Docker socket (e.g., /var/run/docker.sock or http://docker-socket-proxy:2375)." }, "category": { "type": "string", "description": "Filter to only the containers which have this category specified via the glance.category label." }, "running-only": { "type": "boolean", "description": "Whether to only show running containers. If set to true only containers that are currently running will be displayed. If set to false all containers will be displayed regardless of their state." }, "containers": { "type": "object", "description": "Per-container customization. Each key is the container name, and the value is an object with the same properties as the labels (without the \"glance.\" prefix).\n", "additionalProperties": { "type": "object", "properties": { "name": { "type": "string", "description": "The name displayed in the UI. If not specified, the name of the container will be used." }, "icon": { "type": "string", "description": "The icon displayed in the UI. Can be an external URL or an icon prefixed with si:, sh: or di:." }, "url": { "type": "string", "description": "The URL that the user will be redirected to when clicking on the container." }, "same-tab": { "type": "boolean", "description": "Whether to open the link in the same or a new tab. Default is false." }, "description": { "type": "string", "description": "A short description displayed in the UI. Default is empty." }, "hide": { "type": "boolean", "description": "Whether to hide the container. If set to true the container will not be displayed. Defaults to false." }, "id": { "type": "string", "description": "The custom ID of the container. Used to group containers under a single parent." }, "parent": { "type": "string", "description": "The ID of the parent container. Used to group containers under a single parent." }, "category": { "type": "string", "description": "The category of the container. Used to filter containers by category." } } } }, "type": { "enum": [ "docker-containers" ] } } }, { "$ref": "#/definitions/include" } ] }, "iframe-widget": { "oneOf": [ { "required": [ "source" ], "properties": { "source": { "type": "string", "description": "The source of the iframe." }, "height": { "type": "integer", "minimum": 50, "description": "The height of the iframe." }, "type": { "enum": [ "iframe" ] } } }, { "$ref": "#/definitions/include" } ] }, "twitch-channels-widget": { "oneOf": [ { "required": [ "channels" ], "properties": { "channels": { "type": "array", "description": "A list of channels to display.", "items": { "type": "string" } }, "collapse-after": { "type": "integer", "description": "How many channels are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "sort-by": { "enum": [ "viewers", "live" ], "description": "Can be used to specify the order in which the channels are displayed." }, "type": { "enum": [ "twitch-channels" ] } } }, { "$ref": "#/definitions/include" } ] }, "bookmarks-widget": { "oneOf": [ { "required": [ "groups" ], "properties": { "groups": { "type": "array", "description": "An array of groups which can optionally have a title and a custom color.", "items": { "type": "object", "required": [ "links" ], "properties": { "title": { "type": "string", "description": "The title of the group." }, "color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "errorMessage": "Must be a valid HSL color in the format of \"H S L\".", "description": "The colour of the title and the arrow of the group. The value should be a valid HSL color in the format of \"H S L\"." }, "same-tab": { "type": "boolean", "description": "Whether to open the link in the same tab or a new one." }, "hide-arrow": { "type": "boolean", "description": "Whether to hide the colored arrow on each link." }, "target": { "enum": [ "_blank", "_self", "_parent", "_top" ], "description": "Set a custom value for the link's target attribute. This property has precedence over same-tab." }, "links": { "type": "array", "description": "An array of links to display in the group.", "items": { "type": "object", "required": [ "title", "url" ], "properties": { "title": { "type": "string", "description": "The title of the link." }, "url": { "type": "string", "description": "The URL of the link." }, "icon": { "type": "string", "description": "URL pointing to an image. You can also directly use Simple Icons via a si: prefix or Dashboard Icons via a di: prefix:" }, "same-tab": { "type": "boolean", "description": "Whether to open the link in the same tab or a new one." }, "hide-arrow": { "type": "boolean", "description": "Whether to hide the colored arrow on each link." }, "target": { "enum": [ "_blank", "_self", "_parent", "_top" ], "description": "Set a custom value for the link's target attribute. This property has precedence over same-tab." } } } } } } }, "type": { "enum": [ "bookmarks" ] } } }, { "$ref": "#/definitions/include" } ] }, "change-detection-widget": { "oneOf": [ { "properties": { "instance-url": { "type": "string", "description": "The URL pointing to your instance of changedetection.io." }, "token": { "type": "string", "description": "The API access token which can be found in SETTINGS > API. Optionally, you can specify this using an environment variable with the syntax ${VARIABLE_NAME}." }, "limit": { "type": "integer", "description": "The maximum number of watches to show." }, "collapse-after": { "type": "integer", "description": "How many watches are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "watches": { "type": "array", "description": "By default all of the configured watches will be shown. Optionally, you can specify a list of UUIDs for the specific watches you want to have listed", "items": { "type": "string" } }, "type": { "enum": [ "change-detection" ] } } }, { "$ref": "#/definitions/include" } ] }, "twitch-top-games-widget": { "oneOf": [ { "properties": { "exclude": { "type": "array", "items": { "type": "string" }, "description": "A list of categories that will never be shown. You must provide the slug found by clicking on the category and looking at the URL." }, "limit": { "type": "integer", "description": "The maximum number of games to show." }, "collapse-after": { "type": "integer", "description": "How many games are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "type": { "enum": [ "twitch-top-games" ] } } }, { "$ref": "#/definitions/include" } ] }, "server-stats-widget": { "oneOf": [ { "properties": { "servers": { "type": "array", "description": "If not provided it will display the statistics of the server Glance is running on.", "items": { "type": "object", "required": [ "type" ], "properties": { "type": { "enum": [ "local", "remote" ], "description": "Whether to display statistics for the local server or a remote server." }, "name": { "type": "string", "description": "The name of the server which will be displayed on the widget. If not provided it will default to the server's hostname." }, "hide-swap": { "type": "boolean", "description": "Whether to hide the swap usage." }, "cpu-temp-sensor": { "type": "string", "description": "The name of the sensor to use for the CPU temperature. When not provided the widget will attempt to find the correct one, if it fails to do so the temperature will not be displayed. To view the available sensors you can use sensors command" }, "hide-mountpoints-by-default": { "type": "boolean", "description": "If set to true you'll have to manually make each mountpoint visible by adding a hide: false property." }, "mountpoints": { "type": "object", "description": "A map of mountpoints to display disk usage for. The key is the path to the mountpoint and the value is an object with optional properties.", "additionalProperties": { "type": "object", "properties": { "name": { "type": "string", "description": "The name of the mountpoint which will be displayed on the widget. If not provided it will default to the mountpoint's path." }, "hide": { "type": "boolean", "description": "Whether to hide this mountpoint from the widget." } } } }, "url": { "type": "string", "description": "The URL and port of the server to fetch the statistics from." }, "token": { "type": "string", "description": "The authentication token to use when fetching the statistics." }, "timeout": { "type": "string", "pattern": "^\\d+[smhd]$", "description": "The maximum time to wait for a response from the server. The value is a string and must be a number followed by one of s, m, h, d.", "examples": [ "10s" ], "errorMessage": "Must be a valid time duration." } } } }, "type": { "enum": [ "server-stats" ] } } }, { "$ref": "#/definitions/include" } ] }, "repository-widget": { "oneOf": [ { "required": [ "repository" ], "properties": { "repository": { "type": "string", "description": "The owner and repository name that will have their information displayed.", "examples": [ "glanceapp/glance" ] }, "token": { "type": "string", "description": "The GitHub token to use for the API requests. Useful to exceed the unathorised rate limit of 60 requests per hour." }, "pull-requests-limit": { "type": "integer", "description": "The maximum number of latest open pull requests to show. Set to -1 to not show any." }, "issues-limit": { "type": "integer", "description": "The maximum number of latest open issues to show. Set to -1 to not show any." }, "commits-limit": { "type": "integer", "description": "The maximum number of latest open issues to show. Set to -1 to not show any." }, "type": { "enum": [ "repository" ] } } }, { "$ref": "#/definitions/include" } ] }, "lobsters-widget": { "oneOf": [ { "properties": { "instance-url": { "type": "string", "description": "The base URL for a lobsters instance hosted somewhere other than on lobste.rs.", "examples": [ "https://www.journalduhacker.net/" ] }, "custom-url": { "type": "string", "description": "A custom URL to retrieve lobsters posts from. If this is specified, the instance-url, sort-by and tags properties are ignored." }, "limit": { "type": "integer", "description": "The maximum number of posts to show." }, "collapse-after": { "type": "integer", "description": "How many posts are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "sort-by": { "enum": [ "hot", "new" ], "description": "The sort order in which posts are returned." }, "tags": { "type": "array", "items": { "type": "string" }, "description": "Limit to posts containing one of the given tags. You cannot specify a sort order when filtering by tags, it will default to hot." }, "type": { "enum": [ "lobsters" ] } } }, { "$ref": "#/definitions/include" } ] }, "dns-stats-widget": { "oneOf": [ { "required": [ "url" ], "properties": { "service": { "enum": [ "adguard", "pihole", "pihole-v6", "technitium" ], "description": "Either adguard, pihole (v5 and below), pihole-v6 (v6+), or technitium." }, "allow-insecure": { "type": "boolean", "description": "Whether to allow invalid/self-signed certificates when making the request to the service." }, "url": { "type": "string", "description": "The base URL of the service." }, "username": { "type": "string", "description": "Only required when using AdGuard Home. The username used to log into the admin dashboard." }, "password": { "type": "string", "description": "Required when using AdGuard Home, where the password is the one used to log into the admin dashboard. Also requried when using Pi-hole major version 6 and above, where the password is the one used to log into the admin dashboard or the application password, which can be found in Settings -> Web Interface / API -> Configure app password." }, "token": { "type": "string", "description": "Required for Pi-hole v5 or earlier and for Technitium. The API token for the service." }, "hide-graph": { "type": "boolean", "description": "Whether to hide the graph showing the number of queries over time." }, "hide-top-domains": { "type": "boolean", "description": "Whether to hide the list of top blocked domains." }, "hour-format": { "enum": [ "12h", "24h" ], "description": "Whether to display the relative time in the graph in 12h or 24h format." }, "type": { "enum": [ "dns-stats" ] } } }, { "$ref": "#/definitions/include" } ] }, "monitor-widget": { "oneOf": [ { "required": [ "sites" ], "properties": { "show-failing-only": { "type": "boolean", "description": "Shows only a list of failing sites when set to true." }, "style": { "enum": [ "compact" ], "description": "Used to change the appearance of the widget. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#style-3 for examples." }, "sites": { "type": "array", "items": { "type": "object", "required": [ "title", "url" ], "properties": { "title": { "type": "string", "description": "The title used to indicate the site." }, "url": { "type": "string", "description": "The public facing URL of a monitored service, the user will be redirected here. If check-url is not specified, this is used as the status check." }, "check-url": { "type": "string", "description": "The URL which will be requested and its response will determine the status of the site. If not specified, the url property is used." }, "error-url": { "type": "string", "description": "If the monitored service returns an error, the user will be redirected here. If not specified, the url property is used." }, "icon": { "type": "string", "description": "Optional URL to an image which will be used as the icon for the site. Can be an external URL or internal via server configured assets. You can also directly use Simple Icons via a si: prefix or Dashboard Icons via a di: prefix:" }, "allow-insecure": { "type": "boolean", "description": "Whether to ignore invalid/self-signed certificates." }, "same-tab": { "type": "boolean", "description": "Whether to open the link in the same or a new tab." }, "alt-status-codes": { "type": "array", "description": "Status codes other than 200 that you want to return \"OK\".", "items": { "type": "integer" } }, "basic-auth": { "type": "object", "description": "Basic authentication credentials for the site.", "properties": { "username": { "type": "string", "description": "Username for basic authentication." }, "password": { "type": "string", "description": "Password for basic authentication." } } }, "timeout": { "type": "string", "pattern": "^\\d+[smhd]$", "description": "The maximum time to wait for a response from the site. The value is a string and must be a number followed by one of s, m, h, d.", "examples": [ "10s" ], "errorMessage": "Must be a valid time duration." } } } }, "type": { "enum": [ "monitor" ] } } }, { "$ref": "#/definitions/include" } ] }, "releases-widget": { "oneOf": [ { "required": [ "repositories" ], "properties": { "repositories": { "type": "array", "description": "A list of repositores to fetch the latest release for. Only the name/repo is required, not the full URL. A prefix can be specified for repositories hosted elsewhere such as GitLab, Codeberg and Docker Hub. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#repositories", "examples": [ "dockerhub:glanceapp/glance" ], "items": { "oneOf": [ { "type": "string" }, { "type": "object", "required": [ "repository" ], "properties": { "repository": { "type": "string", "description": "The repository to fetch with prereleases enabled. Only available for Github repositories.", "examples": [ "glanceapp/glance" ] }, "include-prereleases": { "type": "boolean", "description": "Whether to include prereleases in the list of releases. Only available for Github repositories." } } } ] } }, "show-source-icon": { "type": "boolean", "description": "Shows an icon of the source (GitHub/GitLab/Codeberg/Docker Hub) next to the repository name when set to true." }, "token": { "type": "string", "description": "The github token to use for fetching releases. Useful to exceed the unauthorised rate limit of 60 requests per hour." }, "gitlab-token": { "type": "string", "description": "The GitLab token to use for fetching releases. Useful to exceed the unauthorised rate limit." }, "limit": { "type": "integer", "description": "The maximum number of releases to show." }, "collapse-after": { "type": "integer", "description": "How many releases are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "type": { "enum": [ "releases" ] } } }, { "$ref": "#/definitions/include" } ] }, "html-widget": { "oneOf": [ { "required": [ "source" ], "properties": { "source": { "type": "string", "description": "The HTML source code to render. Javascript should be included in the document head." }, "type": { "enum": [ "html" ] } } }, { "$ref": "#/definitions/include" } ] }, "custom-api-widget": { "oneOf": [ { "required": [ "template" ], "properties": { "url": { "type": "string", "description": "The URL to fetch the data from. It must be accessible from the server that Glance is running on." }, "headers": { "type": "object", "description": "Optionally specify headers that will be sent with the request.", "examples": [ "x-api-key: your-api-key" ], "additionalProperties": { "type": "string" } }, "method": { "enum": [ "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD" ], "default": "GET", "description": "The HTTP method to use when making the request." }, "body-type": { "enum": [ "json", "string" ], "default": "json", "description": "The type of the body that will be sent with the request." }, "body": { "type": [ "object", "string", "array" ], "description": "The body that will be sent with the request. Can be a string, map, or array depending on body-type." }, "frameless": { "type": "boolean", "default": true, "description": "When set to true, removes the border and padding around the widget." }, "allow-insecure": { "type": "boolean", "default": true, "description": "Whether to ignore invalid/self-signed certificates." }, "skip-json-validation": { "type": "boolean", "default": true, "description": "When set to true, skips the JSON validation step. This is useful when the API returns JSON Lines/newline-delimited JSON, which is a format that consists of several JSON objects separated by newlines." }, "template": { "type": "string", "description": "The template that will be used to display the data. Uses Go's html/template package and tidwall's gjson package." }, "parameters": { "type": "object", "description": "A list of keys and values that will be sent to the custom-api as query parameters.", "examples": [ "key: value" ], "additionalProperties": true }, "subrequests": { "type": "object", "description": "A map of additional requests that will be executed concurrently and made available in the template via the .Subrequest property.", "additionalProperties": { "type": "object", "required": [ "url" ], "properties": { "url": { "type": "string", "description": "The URL to fetch the data from." }, "headers": { "type": "object", "description": "Optionally specify headers that will be sent with the request.", "examples": [ "x-api-key: your-api-key" ], "additionalProperties": { "type": "string" } }, "method": { "enum": [ "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD" ], "default": "GET", "description": "The HTTP method to use when making the request." }, "body-type": { "enum": [ "json", "string" ], "default": "json", "description": "The type of the body that will be sent with the request." }, "body": { "type": [ "object", "string", "array" ], "description": "The body that will be sent with the request. Can be a string, map, or array depending on body-type." }, "allow-insecure": { "type": "boolean", "default": false, "description": "Whether to ignore invalid/self-signed certificates." }, "parameters": { "type": "object", "description": "A list of keys and values that will be sent as query parameters.", "examples": [ "key: value" ], "additionalProperties": true } } } }, "options": { "type": "object", "description": "Custom preferences accessible by the custom-api widget template.", "additionalProperties": true }, "type": { "enum": [ "custom-api" ] } } }, { "$ref": "#/definitions/include" } ] }, "videos-widget": { "oneOf": [ { "required": [ "channels" ], "properties": { "channels": { "type": "array", "description": "A list of channels IDs. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#channels for how to get a channel ID.", "items": { "type": "string" } }, "playlists": { "type": "array", "description": "https://github.com/glanceapp/glance/blob/main/docs/configuration.md#channels", "items": { "type": "string" } }, "limit": { "type": "integer", "description": "The maximum number of videos to show." }, "style": { "enum": [ "horizontal-cards", "vertical-list", "grid-cards" ], "description": "Used to change the appearance of the widget. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#style-1 for examples." }, "collapse-after": { "type": "integer", "description": "Specify the number of videos to show when using the vertical-list style before the \"SHOW MORE\" button appears." }, "collapse-after-rows": { "type": "integer", "description": "Specify the number of rows to show when using the grid-cards style before the \"SHOW MORE\" button appears." }, "include-shorts": { "type": "boolean", "description": "Whether to include YouTube Shorts videos in the widget." }, "video-url-template": { "type": "string", "pattern": ".*\\{VIDEO-ID\\}.*", "description": "Used to replace the default link for videos. Useful when you're running your own YouTube front-end.", "examples": [ "https://invidious.your-domain.com/watch?v={VIDEO-ID}" ], "errorMessage": "Must be a valid video url template. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#video-url-template" }, "type": { "enum": [ "videos" ] } } }, { "$ref": "#/definitions/include" } ] }, "calendar-widget": { "oneOf": [ { "properties": { "first-day-of-week": { "enum": [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ], "description": "The day of the week that the calendar starts on." }, "type": { "enum": [ "calendar" ] } } }, { "$ref": "#/definitions/include" } ] }, "reddit-widget": { "oneOf": [ { "required": [ "subreddit" ], "properties": { "subreddit": { "type": "string", "description": "The subreddit for which to fetch the posts from." }, "style": { "enum": [ "vertical-list", "horizontal-cards", "vertical-cards" ], "description": "Used to change the appearance of the widget. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#style-2 for examples." }, "show-thumbnails": { "type": "boolean", "description": "Shows or hides thumbnails next to the post. This only works if the style is vertical-list." }, "show-flairs": { "type": "boolean", "description": "Shows post flairs when set to true." }, "limit": { "type": "integer", "description": "The maximum number of posts to show." }, "collapse-after": { "type": "integer", "description": "How many posts are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse. Not available when using the vertical-cards and horizontal-cards styles." }, "comments-url-template": { "type": "string", "pattern": ".*(?:\\{POST-PATH\\}|\\{POST-ID\\}|\\{SUBREDDIT\\}).*", "description": "Used to replace the default link for post comments. Useful if you want to use the old Reddit design or any other 3rd party front-end.", "examples": [ "https://old.reddit.com/{POST-PATH}" ], "errorMessage": "Must be a valid comments url template. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#comments-url-template-1" }, "request-url-template": { "type": "string", "pattern": ".*\\{REQUEST-URL\\}.*", "description": "A custom request URL that will be used to fetch the data. This is useful when you're hosting Glance on a VPS where Reddit is blocking the requests and you want to route them through a proxy that accepts the URL as either a part of the path or a query parameter.", "examples": [ "https://proxy/{REQUEST-URL}", "https://your.proxy/?url={REQUEST-URL}" ], "errorMessage": "Must be a valid request url template. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#request-url-template" }, "proxy": { "description": "A custom HTTP/HTTPS proxy URL that will be used to fetch the data. This is useful when you're hosting Glance on a VPS where Reddit is blocking the requests and you want to bypass the restriction by routing the requests through a proxy.", "examples": [ "http://user:pass@proxy.com:8080" ], "oneOf": [ { "type": "string" }, { "type": "object", "properties": { "url": { "type": "string", "description": "A custom HTTP/HTTPS proxy URL that will be used to fetch the data. This is useful when you're hosting Glance on a VPS where Reddit is blocking the requests and you want to bypass the restriction by routing the requests through a proxy.", "examples": [ "http://user:pass@proxy.com:8080" ] }, "allow-insecure": { "type": "boolean", "description": "When set to true, allows the use of insecure connections such as when the proxy has a self-signed certificate." }, "timeout": { "type": "string", "pattern": "^\\d+[smhd]$", "description": "The maximum time to wait for a response from the proxy. The value is a string and must be a number followed by one of s, m, h, d.", "examples": [ "10s" ], "errorMessage": "Must be a valid time duration." } } } ] }, "sort-by": { "enum": [ "hot", "new", "top", "rising" ], "description": "Can be used to specify the order in which the posts should get returned." }, "top-period": { "enum": [ "hour", "day", "week", "month", "year", "all" ], "description": "Can be used to specify the order in which the posts should get returned." }, "search": { "type": "string", "description": "Keywords to search for. Searching within specific fields is also possible, though keep in mind that Reddit may remove the ability to use any of these at any time" }, "extra-sort-by": { "enum": [ "engagement" ], "description": "Can be used to specify an additional sort which will be applied on top of the already sorted posts." }, "app-auth": { "type": "object", "description": "Reddit API credentials for app authentication.", "properties": { "client-id": { "type": "string", "description": "Reddit API client ID." }, "client-secret": { "type": "string", "description": "Reddit API client secret." }, "username": { "type": "string", "description": "Reddit username for authentication." }, "password": { "type": "string", "description": "Reddit password for authentication." } } }, "type": { "enum": [ "reddit" ] } } }, { "$ref": "#/definitions/include" } ] }, "clock-widget": { "oneOf": [ { "properties": { "hour-format": { "enum": [ "12h", "24h" ], "description": "Whether to show the time in 12 or 24 hour format." }, "timezones": { "type": "array", "items": { "type": "object", "required": [ "timezone" ], "properties": { "timezone": { "type": "string", "description": "A timezone identifier such as Europe/London, America/New_York, etc. The full list of available identifiers can be found here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones." }, "label": { "type": "string", "description": "Optionally, override the display value for the timezone to something more meaningful such as \"Home\", \"Work\" or anything else." } } } }, "type": { "enum": [ "clock" ] } } }, { "$ref": "#/definitions/include" } ] }, "markets-widget": { "oneOf": [ { "required": [ "markets" ], "properties": { "markets": { "type": "array", "description": "An array of markets for which to display information about.", "items": { "type": "object", "required": [ "symbol" ], "properties": { "symbol": { "type": "string", "description": "The symbol, as seen in Yahoo Finance." }, "name": { "type": "string", "description": "The name that will be displayed under the symbol." }, "symbol-link": { "type": "string", "description": "The link to go to when clicking on the symbol." }, "chart-link": { "type": "string", "description": "The link to go to when clicking on the chart." } } } }, "sort-by": { "enum": [ "change", "absolute-change" ], "description": "By default the markets are displayed in the order they were defined. You can customize their ordering by setting the sort-by property to change for descending order based on the stock's percentage change (e.g. 1% would be sorted higher than -1%) or absolute-change for descending order based on the stock's absolute price change (e.g. -1% would be sorted higher than +0.5%)." }, "chart-link-template": { "type": "string", "pattern": ".*\\{SYMBOL\\}.*", "description": "A template for the link to go to when clicking on the chart that will be applied to all markets. The value {SYMBOL} will be replaced with the symbol of the market. You can override this on a per-market basis by specifying a chart-link property.", "examples": [ "https://www.tradingview.com/chart/?symbol={SYMBOL}" ], "errorMessage": "Must be a valid chart link template. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#chart-link-template" }, "symbol-link-template": { "type": "string", "pattern": ".*\\{SYMBOL\\}.*", "description": "A template for the link to go to when clicking on the symbol that will be applied to all markets. The value {SYMBOL} will be replaced with the symbol of the market. You can override this on a per-market basis by specifying a symbol-link property.", "examples": [ "https://www.google.com/search?tbm=nws&q={SYMBOL}" ], "errorMessage": "Must be a valid symbol link template. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#symbol-link-template" }, "type": { "enum": [ "markets" ] } } }, { "$ref": "#/definitions/include" } ] }, "weather-widget": { "oneOf": [ { "required": [ "location" ], "properties": { "location": { "type": "string", "description": "The name of the city and country to fetch weather information for. See https://open-meteo.com/en/docs/geocoding-api." }, "units": { "enum": [ "metric", "imperial" ], "description": "Whether to show the temperature in celsius or fahrenheit, possible values are metric or imperial." }, "hour-format": { "enum": [ "12h", "24h" ], "description": "Whether to show the hours of the day in 12-hour format or 24-hour format." }, "hide-location": { "type": "boolean", "description": "Optionally don't display the location name on the widget." }, "show-area-name": { "type": "boolean", "description": "Whether to display the state/administrative area in the location name." }, "type": { "enum": [ "weather" ] } } }, { "$ref": "#/definitions/include" } ] }, "search-widget": { "oneOf": [ { "properties": { "search-engine": { "description": "Either a value from the table below or a URL to a custom search engine. Use {QUERY} to indicate where the query value gets placed.", "oneOf": [ { "enum": [ "duckduckgo", "google" ] }, { "type": "string", "pattern": ".*\\{QUERY\\}.*" } ] }, "new-tab": { "type": "boolean", "description": "When set to true, swaps the shortcuts for showing results in the same or new tab, defaulting to showing results in a new tab." }, "autofocus": { "type": "boolean", "description": "When set to true, automatically focuses the search input on page load." }, "target": { "type": "string", "enum": [ "_blank", "_self", "_parent", "_top" ], "description": "The target to use when opening the search results in a new tab." }, "placeholder": { "type": "string", "description": "When set, modifies the text displayed in the input field before typing." }, "bangs": { "type": "array", "description": "A list of bangs to use with the search engine. See https://duckduckgo.com/bangs", "items": { "type": "object", "required": [ "shortcut", "url" ], "properties": { "title": { "type": "string", "description": "Optional title that will appear on the right side of the search bar when the query starts with the associated shortcut." }, "shortcut": { "type": "string", "description": "Any value you wish to use as the shortcut for the search engine. It does not have to start with !.", "examples": [ "!yt" ] }, "url": { "type": "string", "pattern": ".*\\{QUERY\\}.*", "description": "The URL of the search engine. Use {QUERY} to indicate where the query value gets placed.", "examples": [ "https://www.youtube.com/results?search_query={QUERY}" ], "errorMessage": "Must be a valid URL with {QUERY} as a placeholder for the search query." } } } }, "type": { "enum": [ "search" ] } } }, { "$ref": "#/definitions/include" } ] }, "rss-widget": { "oneOf": [ { "required": [ "feeds" ], "properties": { "style": { "enum": [ "vertical-list", "detailed-list", "horizontal-cards", "horizontal-cards-2" ], "description": "Used to change the appearance of the widget. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#style for examples." }, "feeds": { "type": "array", "description": "An array of RSS/atom feeds. The title can optionally be changed.", "items": { "type": "object", "required": [ "url" ], "properties": { "url": { "type": "string", "description": "The URL of the RSS feed." }, "title": { "type": "string", "description": "The title of the feed. If not provided, Glance will try to fetch the title from the feed." }, "hide-categories": { "type": "boolean", "description": "When set to true, hides the categories of the articles." }, "hide-description": { "type": "boolean", "description": "When set to true, hides the description of the articles." }, "limit": { "type": "integer", "description": "The maximum number of articles to show from that specific feed. Useful if you have a feed which posts a lot of articles frequently and you want to prevent it from excessively pushing down articles from other feeds." }, "item-link-prefix": { "type": "string", "description": "If an RSS feed isn't returning item links with a base domain and Glance has failed to automatically detect the correct domain you can manually add a prefix to each link with this property." }, "headers": { "type": "object", "description": "Optionally specify the headers that will be sent with the request.", "examples": [ "x-api-key: your-api-key" ], "additionalProperties": { "type": "string" } } } } }, "thumbnail-height": { "type": "number", "description": "Used to modify the height of the thumbnails. Works only when the style is set to horizontal-cards. The default value is 10 and the units are rem, if you want to for example double the height of the thumbnails you can set it to 20." }, "card-height": { "type": "number", "description": "Used to modify the height of cards when using the horizontal-cards-2 style. The default value is 27 and the units are rem." }, "limit": { "type": "integer", "description": "The maximum number of articles to show." }, "preserve-order": { "type": "boolean", "description": "When set to true, the order of the articles will be preserved as they are in the feeds. Useful if a feed uses its own sorting order which denotes the importance of the articles. If you use this property while having a lot of feeds, it's recommended to set a limit to each individual feed since if the first defined feed has 15 articles, the articles from the second feed will start after the 15th article in the list." }, "single-line-titles": { "type": "boolean", "description": "When set to true, truncates the title of each post if it exceeds one line. Only applies when the style is set to vertical-list." }, "collapse-after": { "type": "integer", "description": "How many articles are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "type": { "enum": [ "rss" ] } } }, { "$ref": "#/definitions/include" } ] }, "to-do-widget": { "oneOf": [ { "properties": { "id": { "type": "string", "description": "Required if multiple to-do widgets are present. Used to distinguish between them." }, "type": { "enum": [ "to-do" ] } } }, { "$ref": "#/definitions/include" } ] }, "group-widget": { "oneOf": [ { "required": [ "widgets" ], "properties": { "define": { "type": "object", "additionalProperties": true, "description": "See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#sharing-properties" }, "widgets": { "type": "array", "description": "A list of widgets in the same format as on a page column. Accepts any widget apart from a split column or group widget.", "items": { "allOf": [ { "$ref": "#/definitions/widget-item" }, { "not": { "properties": { "type": { "enum": [ "group", "split-column" ] } } } } ] } }, "type": { "enum": [ "group" ] } } }, { "$ref": "#/definitions/include" } ] }, "hacker-news-widget": { "oneOf": [ { "properties": { "limit": { "type": "integer", "description": "The maximum number of posts to show." }, "collapse-after": { "type": "integer", "description": "How many posts are visible before the \"SHOW MORE\" button appears. Set to -1 to never collapse." }, "comments-url-template": { "type": "string", "pattern": ".*\\{POST-ID\\}.*", "description": "Used to replace the default link for post comments. Useful if you want to use an alternative front-end.", "examples": [ "https://www.hckrnws.com/stories/{POST-ID}" ], "errorMessage": "Must be a valid comments url template. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#comments-url-template" }, "sort-by": { "enum": [ "top", "new", "best" ], "description": "Used to specify the order in which the posts should get returned." }, "extra-sort-by": { "enum": [ "engagement" ], "description": "Can be used to specify an additional sort which will be applied on top of the already sorted posts." }, "type": { "enum": [ "hacker-news" ] } } }, { "$ref": "#/definitions/include" } ] }, "widget-item": { "oneOf": [ { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/split-column-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/extension-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/docker-containers-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/iframe-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/twitch-channels-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/bookmarks-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/change-detection-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/twitch-top-games-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/server-stats-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/repository-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/lobsters-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/dns-stats-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/monitor-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/releases-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/html-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/custom-api-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/videos-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/calendar-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/reddit-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/clock-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/markets-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/weather-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/search-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/rss-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/to-do-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/group-widget" } ] }, { "allOf": [ { "$ref": "#/definitions/widget-base" }, { "$ref": "#/definitions/hacker-news-widget" } ] }, { "$ref": "#/definitions/include" } ] }, "widget": { "type": "array", "items": { "$ref": "#/definitions/widget-item" } }, "page-item": { "type": "object", "properties": { "name": { "type": "string", "description": "The name of the page which gets shown in the navigation bar." }, "slug": { "type": "string", "description": "The URL friendly version of the title which is used to access the page. For example if the title of the page is \"RSS Feeds\" you can make the page accessible via localhost:8080/feeds by setting the slug to feeds. If not defined, it will automatically be generated from the title." }, "width": { "enum": [ "slim", "wide" ], "description": "The maximum width of the page on desktop. Default is 1600px, slim is 1100px and wide is 1920px. A slime page can only have 2 columns." }, "center-vertically": { "type": "boolean", "description": "When set to true, vertically centers the content on the page. Has no effect if the content is taller than the height of the viewport." }, "hide-desktop-navigation": { "type": "boolean", "description": "Whether to show the navigation links at the top of the page on desktop." }, "expand-mobile-page-navigation": { "type": "boolean", "description": "Whether the mobile page navigation should be expanded by default." }, "show-mobile-header": { "type": "boolean", "description": "Whether to show a header displaying the name of the page on mobile. The header purposefully has a lot of vertical whitespace in order to push the content down and make it easier to reach on tall devices." }, "desktop-navigation-width": { "enum": [ "default", "slim", "wide" ], "description": "The width of the navigation bar on desktop. Can be 'default', 'slim', or 'wide'.\nIf not set, inherits from the 'width' property.\n" }, "columns": { "type": "array", "description": "A list of columns to display on the page.", "items": { "type": "object", "required": [ "size" ], "properties": { "size": { "enum": [ "full", "small" ], "description": "The size of the column. A small column takes up a fixed amount of width (300px) and a full column takes up the all of the remaining width. A page can have at most two full columns." }, "widgets": { "type": "array", "additionalItems": false, "description": "A list of widgets to display in the column.", "items": { "$ref": "#/definitions/widget-item" } } } } }, "head-widgets": { "type": "array", "description": "An array of widgets that will appear above the columns on this page. Each item should be a widget object.\n", "items": { "$ref": "#/definitions/widget-item" } } } }, "page": { "type": "array", "items": { "$ref": "#/definitions/page-item" } }, "branding-item": { "type": "object", "properties": { "hide-footer": { "type": "boolean", "description": "Hides the footer when set to true." }, "custom-footer": { "type": "string", "description": "Specify custom HTML to use for the footer." }, "logo-text": { "type": "string", "description": "Specify custom text to use instead of the \"G\" found in the navigation." }, "logo-url": { "type": "string", "description": "Specify a URL to a custom image to use instead of the \"G\" found in the navigation. If both logo-text and logo-url are set, only logo-url will be used." }, "favicon-url": { "type": "string", "description": "Specify a URL to a custom image to use for the favicon." }, "app-name": { "type": "string", "description": "Name of the app for PWA installation and display." }, "app-icon": { "type": "string", "description": "URL to the app icon for PWA installation." }, "app-background-color": { "type": "string", "description": "Background color for the PWA splash screen (CSS color value)." } }, "additionalProperties": false }, "branding": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/branding-item" }, { "$ref": "#/definitions/include" } ] } }, "theme-item": { "type": "object", "properties": { "light": { "type": "boolean", "description": "Whether the scheme is light or dark. This does not change the background color, it inverts the text colors so that they look appropriately on a light background.", "default": false }, "background-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Color of the page and widgets.", "default": "240 8 9", "examples": [ "240 8 9" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "primary-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Color used across the page, largely to indicate unvisited links.", "default": "43 50 70", "examples": [ "43 50 70" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "positive-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Used to indicate that something is positive, such as stock price being up, twitch channel being live or a monitored site being online. If not set, the value of primary-color will be used.", "default": "", "examples": [ "43 50 70" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "negative-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Oppposite of positive-color.", "default": "0 70 70", "examples": [ "0 70 70" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "contrast-multiplier": { "type": "number", "description": "Used to increase or decrease the contrast (in other words visibility) of the text. A value of 1.3 means that the text will be 30% lighter/darker depending on the scheme.", "default": 1 }, "text-saturation-multiplier": { "type": "number", "description": "Used to increase or decrease the saturation of text, useful when using a custom background color with a high amount of saturation and needing the text to have a more neutral color. 0.5 means that the saturation will be 50% lower and 1.5 means that it'll be 50% higher.", "default": 1 }, "custom-css-file": { "type": "string", "pattern": "^.*\\.css$", "description": "Path to a custom CSS file, either external or one from within the server configured assets path.", "examples": [ "/assets/my-style.css" ], "errorMessage": "Must be a file path to a CSS file ending in \".css\"." }, "disable-picker": { "type": "boolean", "description": "When set to true hides the theme picker and disables the ability to switch between themes. All users who previously picked a non-default theme will be switched over to the default theme.", "default": false }, "presets": { "type": "object", "description": "Define additional theme presets that can be selected from the theme picker on the page. For each preset, you can specify the same properties as for the default theme, except for the custom-css-file property.", "additionalProperties": { "type": "object", "properties": { "light": { "type": "boolean", "description": "Whether the scheme is light or dark. This does not change the background color, it inverts the text colors so that they look appropriately on a light background.", "default": false }, "background-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Color of the page and widgets.", "examples": [ "240 8 9" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "primary-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Color used across the page, largely to indicate unvisited links.", "examples": [ "43 50 70" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "positive-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Used to indicate that something is positive, such as stock price being up, twitch channel being live or a monitored site being online. If not set, the value of primary-color will be used.", "examples": [ "43 50 70" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "negative-color": { "type": "string", "pattern": "^\\d{1,3} \\d{1,3} \\d{1,3}$", "description": "Oppposite of positive-color.", "examples": [ "0 70 70" ], "errorMessage": "Must be a valid HSL color in the format of \"H S L\"." }, "contrast-multiplier": { "type": "number", "description": "Used to increase or decrease the contrast (in other words visibility) of the text. A value of 1.3 means that the text will be 30% lighter/darker depending on the scheme." }, "text-saturation-multiplier": { "type": "number", "description": "Used to increase or decrease the saturation of text, useful when using a custom background color with a high amount of saturation and needing the text to have a more neutral color. 0.5 means that the saturation will be 50% lower and 1.5 means that it'll be 50% higher." } } } } }, "additionalProperties": false }, "theme": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/theme-item" }, { "$ref": "#/definitions/include" } ] } }, "document-item": { "type": "object", "properties": { "head": { "type": "string", "description": "The HTML to be included in the head of the document." } }, "additionalProperties": false }, "document": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/document-item" }, { "$ref": "#/definitions/include" } ] } }, "server-item": { "type": "object", "properties": { "host": { "type": "string", "description": "The address which the server will listen on. Setting it to localhost means that only the machine that the server is running on will be able to access the dashboard. By default it will listen on all interfaces." }, "port": { "type": "integer", "description": "The port which the server will listen on. By default it will listen on port 8000.", "minimum": 1, "maximum": 65535 }, "base-url": { "type": "string", "description": "The base URL that Glance is hosted under. No need to specify this unless you're using a reverse proxy and are hosting Glance under a directory. If that's the case then you can set this value to /glance or whatever the directory is called. Note that the forward slash (/) in the beginning is required unless you specify the full domain and path" }, "assets-path": { "type": "string", "description": "The path to a directory that will be served by the server under the /assets/ path.", "examples": [ "/app/assets" ] } }, "additionalProperties": false }, "server": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/server-item" }, { "$ref": "#/definitions/include" } ] } }, "auth-item": { "type": "object", "properties": { "secret-key": { "type": "string", "description": "Secret key for session signing. See https://github.com/glanceapp/glance/blob/main/docs/configuration.md#authentication." }, "users": { "type": "object", "description": "Map of usernames to authentication details. Each user can have either a password (plain or env var) or a password hash.", "additionalProperties": { "type": "object", "properties": { "password": { "type": "string", "description": "Password for the user. Can use env var syntax (e.g., ${ADMIN_PASSWORD})." }, "password-hash": { "type": "string", "description": "Bcrypt hash of the user's password." } }, "oneOf": [ { "required": [ "password" ] }, { "required": [ "password-hash" ] } ] } } }, "required": [ "secret-key", "users" ], "additionalProperties": false }, "auth": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/auth-item" }, { "$ref": "#/definitions/include" } ] } } } }