瀏覽代碼

Documentation cleanup (#22127)

Jeremy Stretch 2 周之前
父節點
當前提交
e50aff8736

+ 4 - 4
docs/configuration/error-reporting.md

@@ -18,7 +18,7 @@ Additionally, `http_proxy` and `https_proxy` are set to the HTTP and HTTPS proxi
 
 ## SENTRY_DSN
 
-!!! warning "This parameter will be removed in NetBox v4.5."
+!!! warning "This parameter will be removed in NetBox v4.7."
     Set this using `SENTRY_CONFIG` instead:
 
     ```
@@ -50,7 +50,7 @@ Set to `True` to enable automatic error reporting via [Sentry](https://sentry.io
 
 ## SENTRY_SAMPLE_RATE
 
-!!! warning "This parameter will be removed in NetBox v4.5."
+!!! warning "This parameter will be removed in NetBox v4.7."
     Set this using `SENTRY_CONFIG` instead:
 
     ```
@@ -67,7 +67,7 @@ The sampling rate for errors. Must be a value between 0 (disabled) and 1.0 (repo
 
 ## SENTRY_SEND_DEFAULT_PII
 
-!!! warning "This parameter will be removed in NetBox v4.5."
+!!! warning "This parameter will be removed in NetBox v4.7."
     Set this using `SENTRY_CONFIG` instead:
 
     ```
@@ -103,7 +103,7 @@ SENTRY_TAGS = {
 
 ## SENTRY_TRACES_SAMPLE_RATE
 
-!!! warning "This parameter will be removed in NetBox v4.5."
+!!! warning "This parameter will be removed in NetBox v4.7."
     Set this using `SENTRY_CONFIG` instead:
 
     ```

+ 3 - 3
docs/configuration/security.md

@@ -153,7 +153,7 @@ EXEMPT_VIEW_PERMISSIONS = ['*']
 
 Default: `False`
 
-If `True`, the lifetime of a user's authentication session will be automatically reset upon each valid request. For example, if [`LOGIN_TIMEOUT`](#login_timeout) is configured to 14 days (the default), and a user whose session is due to expire in five days makes a NetBox request (with a valid session cookie), the session's lifetime will be reset to 14 days.
+If `True`, the lifetime of a user's authentication session will be automatically reset upon each valid request. For example, if [`LOGIN_TIMEOUT`](#login_timeout) is configured to 14 days, and a user whose session is due to expire in five days makes a NetBox request (with a valid session cookie), the session's lifetime will be reset to 14 days.
 
 Note that enabling this setting causes NetBox to update a user's session in the database (or file, as configured per [`SESSION_FILE_PATH`](#session_file_path)) with each request, which may introduce significant overhead in very active environments. It also permits an active user to remain authenticated to NetBox indefinitely.
 
@@ -175,9 +175,9 @@ When enabled, only authenticated users are permitted to access any part of NetBo
 
 ## LOGIN_TIMEOUT
 
-Default: `1209600` seconds (14 days)
+Default: `None`
 
-The lifetime (in seconds) of the authentication cookie issued to a NetBox user upon login.
+The lifetime (in seconds) of the authentication cookie issued to a NetBox user upon login. If set to `None` (the default), Django's [`SESSION_COOKIE_AGE`](https://docs.djangoproject.com/en/stable/ref/settings/#session-cookie-age) is used, which defaults to two weeks (1,209,600 seconds).
 
 ---
 

+ 6 - 0
docs/features/context-data.md

@@ -84,3 +84,9 @@ Devices and virtual machines may also have a local context data defined. This lo
 
 !!! warning
     If you find that you're routinely defining local context data for many individual devices or virtual machines, [custom fields](./customization.md#custom-fields) may offer a more effective solution.
+
+## Profiles & Schema Validation
+
+A [config context profile](../models/extras/configcontextprofile.md) provides an organizational grouping for related config contexts and may optionally enforce a [JSON schema](https://json-schema.org/) describing the shape of their data. When a profile is assigned to a config context, NetBox validates the context's data against the profile's schema on save and rejects any context that fails validation. This makes it possible to constrain which keys may appear in a context, require certain keys to be present, or limit values to a defined enumeration — guarding against typos and drift as contexts proliferate.
+
+A profile's schema may be authored directly in NetBox or populated from an external [data source](../models/core/datasource.md), enabling teams to maintain schemas alongside the code or configurations that consume them.

+ 19 - 2
docs/features/devices-cabling.md

@@ -6,18 +6,23 @@ NetBox uses device types to represent unique real-world device models. This allo
 
 ```mermaid
 flowchart TD
-    Manufacturer -.-> Platform & DeviceType & ModuleType
+    Manufacturer -.-> Platform
     Manufacturer --> DeviceType & ModuleType
+    ModuleTypeProfile -.-> ModuleType
     DeviceRole & Platform & DeviceType --> Device
     Device & ModuleType ---> Module
     Device & Module --> Interface & ConsolePort & PowerPort & ...
+    Interface --> MACAddress
 
 click Device "../../models/dcim/device/"
 click DeviceRole "../../models/dcim/devicerole/"
 click DeviceType "../../models/dcim/devicetype/"
+click Interface "../../models/dcim/interface/"
+click MACAddress "../../models/dcim/macaddress/"
 click Manufacturer "../../models/dcim/manufacturer/"
 click Module "../../models/dcim/module/"
 click ModuleType "../../models/dcim/moduletype/"
+click ModuleTypeProfile "../../models/dcim/moduletypeprofile/"
 click Platform "../../models/dcim/platform/"
 ```
 
@@ -69,15 +74,23 @@ Sometimes it is necessary to model a set of physical devices as sharing a single
 
 A virtual device context (VDC) is a logical partition within a device. Each VDC operates autonomously but shares a common pool of resources. Each interface can be assigned to one or more VDCs on its device.
 
-## Module Types & Modules
+## Module Types, Profiles & Modules
 
 Much like device types and devices, module types can instantiate discrete modules, which are hardware components installed within devices. Modules often have their own child components, which become available to the parent device. For example, when modeling a chassis-based switch with multiple line cards in NetBox, the chassis would be created (from a device type) as a device, and each of its line cards would be instantiated from a module type as a module installed in one of the device's module bays.
 
+### Module Type Profiles
+
+A [module type profile](../models/dcim/moduletypeprofile.md) classifies module types (e.g. `Power Supply`, `Disk`) and may optionally define a [JSON schema](https://json-schema.org/) describing custom attributes that module types of that profile may carry. This is useful for tracking domain-specific specifications such as a power supply's input voltage, a CPU's clock speed, or a disk's capacity, without needing to add a custom field to every module type in NetBox.
+
 !!! tip "Device Bays vs. Module Bays"
     What's the difference between device bays and module bays? Device bays are appropriate when the installed hardware has its own management plane, isolated from the parent device. A common example is a blade server chassis in which the blades share power but operate independently. In contrast, a module bay holds a module which does _not_ operate independently of its parent device, as with the chassis switch line card example mentioned above.
 
 One especially nice feature of modules is that templated components can be automatically renamed according to the module bay into which the parent module is installed. For example, if we create a module type with interfaces named `Gi{module}/0/1-48` and install a module of this type into module bay 7 of a device, NetBox will create interfaces named `Gi7/0/1-48`.
 
+## MAC Addresses
+
+[MAC addresses](../models/dcim/macaddress.md) are modeled as first-class objects in NetBox so that an interface may have multiple MAC addresses assigned to it, with one optionally designated as the interface's primary MAC. This accommodates virtual interfaces and modular hardware where the link-layer address is not necessarily fixed at the factory. MAC addresses can be assigned to both [device interfaces](../models/dcim/interface.md) and [virtual machine interfaces](../models/virtualization/vminterface.md).
+
 ## Cables
 
 NetBox models cables as connections among certain types of device components and other objects. Each cable can be assigned a type, color, length, and label. NetBox will enforce basic sanity checks to prevent invalid connections. (For example, a network interface cannot be connected to a power outlet.)
@@ -89,3 +102,7 @@ flowchart LR
     Interface --> Cable
     Cable --> fp1[Front Port] & fp2[Front Port]
 ```
+
+### Cable Bundles
+
+Related cables can optionally be grouped into a [cable bundle](../models/dcim/cablebundle.md), representing a logical collection such as a conduit, trunk, or wiring harness. Bundle membership is purely organizational: it does not affect cable tracing or connectivity. Deleting a cable removes it from its bundle but does not delete the bundle itself, allowing bundles to outlive any specific member cable.

+ 7 - 1
docs/features/facilities.md

@@ -13,10 +13,12 @@ flowchart TD
     Rack --> Device
     Site --> Rack
     RackRole --> Rack
+    RackGroup --> Rack
 
 click Device "../../models/dcim/device/"
 click Location "../../models/dcim/location/"
 click Rack "../../models/dcim/rack/"
+click RackGroup "../../models/dcim/rackgroup/"
 click RackRole "../../models/dcim/rackrole/"
 click Region "../../models/dcim/region/"
 click Site "../../models/dcim/site/"
@@ -60,11 +62,15 @@ A location can be any logical subdivision within a building, such as a floor or
 
 A rack type represents a unique specification of a rack which exists in the real world. Each rack type can be setup with weight, height, and unit ordering. New racks of this type can then be created in NetBox, and any associated specifications will be automatically replicated from the device type.
 
+## Rack Groups
+
+In addition to being assigned to a [location](#locations), racks may optionally be assigned to a [rack group](../models/dcim/rackgroup.md). Rack groups are flat (non-hierarchical) and exist alongside locations as a secondary axis of grouping — particularly handy for organizing racks by row, aisle, or pod within a single location, or for scoping [VLAN groups](../models/ipam/vlangroup.md) to a subset of racks.
+
 ## Racks
 
 Finally, NetBox models each equipment rack as a discrete object within a site and location. These are physical objects into which devices are installed. Each rack can be assigned an operational status, type, facility ID, and other attributes related to inventory tracking. Each rack also must define a height (in rack units) and width, and may optionally specify its physical dimensions.
 
-Each rack must be associated to a site, but the assignment to a location within that site is optional. Users can also create custom roles to which racks can be assigned. NetBox supports tracking rack space in half-unit increments, so it's possible to mount devices at e.g. position 2.5 within a rack.
+Each rack must be associated to a site, but the assignment to a location or rack group within that site is optional. Users can also create custom roles to which racks can be assigned. NetBox supports tracking rack space in half-unit increments, so it's possible to mount devices at e.g. position 2.5 within a rack.
 
 !!! tip "Devices"
     You'll notice in the diagram above that a device can be installed within a site, location, or rack. This approach affords plenty of flexibility as not all sites need to define child locations, and not all devices reside in racks.

+ 53 - 0
docs/integrations/rest-api.md

@@ -663,6 +663,51 @@ Note that there is no requirement for the attributes to be identical among objec
 !!! note
     The bulk update of objects is an all-or-none operation, meaning that if NetBox fails to successfully update any of the specified objects (e.g. due a validation error), the entire operation will be aborted and none of the objects will be updated.
 
+### Concurrent Update Protection
+
+!!! info "This feature was introduced in NetBox v4.6."
+
+To guard against the lost-update problem when multiple clients modify the same object, NetBox returns a weak `ETag` response header on detail-view responses (`GET`, `POST`, `PATCH`, `PUT`) for individual objects. Clients may supply this value back on a subsequent `PATCH` or `PUT` request via the `If-Match` request header. If the object's current ETag does not match any of the values supplied, the server rejects the request with a `412 Precondition Failed` response and includes the current ETag in the response so the client can retry.
+
+```no-highlight
+# Capture the ETag returned with the object
+$ curl -s -i -H "Authorization: Bearer $TOKEN" http://netbox/api/dcim/sites/1/ | grep -i ^etag
+ETag: W/"2026-05-01T17:42:11.123456+00:00"
+
+# Submit an update with If-Match referencing that ETag
+$ curl -s -X PATCH \
+    -H "Authorization: Bearer $TOKEN" \
+    -H "Content-Type: application/json" \
+    -H 'If-Match: W/"2026-05-01T17:42:11.123456+00:00"' \
+    http://netbox/api/dcim/sites/1/ \
+    --data '{"status": "decommissioning"}'
+```
+
+A literal `If-Match: *` value matches any current ETag and may be used to assert simply that the object exists. Submitting `If-Match` is optional; requests without the header retain prior (last-write-wins) behavior.
+
+### Adding and Removing Tags
+
+!!! info "This feature was introduced in NetBox v4.6."
+
+In addition to replacing an object's tag set wholesale via the `tags` field, taggable models accept two write-only fields, `add_tags` and `remove_tags`, which apply only the specified additions or removals without disturbing existing tags. This is convenient when concurrent clients each manage a distinct subset of an object's tags.
+
+```no-highlight
+curl -s -X PATCH \
+-H "Authorization: Bearer $TOKEN" \
+-H "Content-Type: application/json" \
+http://netbox/api/dcim/sites/1/ \
+--data '{
+    "add_tags": [{"name": "production"}],
+    "remove_tags": [{"name": "staging"}]
+}'
+```
+
+Constraints:
+
+* `tags` may not be combined with `add_tags` or `remove_tags` in the same request.
+* `remove_tags` is only valid on updates; it cannot be used when creating a new object.
+* The same tag may not appear in both `add_tags` and `remove_tags`.
+
 ### Deleting an Object
 
 To delete an object from NetBox, make a `DELETE` request to the model's _detail_ endpoint specifying its unique numeric ID. The `Authorization` header must be included to specify an authorization token, however this type of request does not support passing any data in the body.
@@ -871,3 +916,11 @@ GET /api/dcim/sites/?created_by_request=e39c84bc-f169-4d5f-bc1c-94487a1b18b5
 
 !!! note
     This header is included with _all_ NetBox responses, although it is most practical when working with an API.
+
+### `ETag`
+
+A weak entity tag (e.g. `W/"2026-05-01T17:42:11.123456+00:00"`) returned on detail-view responses for individual objects. The value is derived from the object's `last_updated` timestamp (or `created`, if the object has no `last_updated`). Clients may supply this value on a subsequent write request via the `If-Match` header to perform a conditional update. See [Concurrent Update Protection](#concurrent-update-protection) for details.
+
+### `If-Match`
+
+A request header which may be supplied on `PATCH` or `PUT` requests targeting a single object. If the object's current ETag does not match any value supplied, the request is rejected with a `412 Precondition Failed` response. A literal value of `*` matches any existing object. See [Concurrent Update Protection](#concurrent-update-protection) for details.

+ 43 - 0
docs/models/core/objectchange.md

@@ -0,0 +1,43 @@
+# Object Changes
+
+An object change is a record of a single create, update, or delete operation against an object whose model supports [change logging](../../features/change-logging.md). Object changes form a complete audit trail: each one captures the user that initiated the change, the request that caused it, the action performed, and a JSON snapshot of the object before and after.
+
+For component objects (e.g. an interface on a device), an object change can also reference a related parent object so that the change appears in the parent's changelog as well as the component's own.
+
+## Fields
+
+### Time
+
+The date and time at which the change was recorded.
+
+### User & User Name
+
+The [user](../users/user.md) who initiated the change. The user's username is also stored as a static string (`user_name`) so that change records remain readable even after the user account is deleted.
+
+### Request ID
+
+A UUID identifying the request that produced the change. All changes resulting from a single request share the same request ID, which makes it easy to correlate related modifications. The same value is returned on REST API responses via the `X-Request-ID` header.
+
+### Action
+
+The type of operation performed: `create`, `update`, or `delete`.
+
+### Changed Object
+
+A generic foreign key (`changed_object_type` + `changed_object_id`) identifying the object that was modified.
+
+### Related Object
+
+An optional generic foreign key referencing a related object (e.g. the parent device for a changed interface). When set, the change is also surfaced in the related object's changelog.
+
+### Object Representation
+
+A static text representation of the changed object, captured at the time of the change. Preserved so that the change record is meaningful even after the underlying object is deleted.
+
+### Message
+
+An optional free-form message attached to the change. May be supplied via the UI (in eligible forms) or via the [REST API](../../integrations/rest-api.md#changelog-messages) using the `changelog_message` field.
+
+### Pre-Change Data & Post-Change Data
+
+JSON snapshots of the object's serialized state immediately before and immediately after the change. For `create` actions, only post-change data is recorded; for `delete` actions, only pre-change data. The diff displayed in the UI is computed from these snapshots.

+ 26 - 0
docs/models/core/objecttype.md

@@ -0,0 +1,26 @@
+# Object Types
+
+An object type identifies a NetBox model by its app label and model name (e.g. `dcim.device`). Object types are used wherever NetBox needs to refer to a model dynamically — most commonly in [custom fields](../extras/customfield.md), [object permissions](../users/objectpermission.md), [export templates](../extras/exporttemplate.md), [event rules](../extras/eventrule.md), and generic relations such as the assignment of an [IP address](../ipam/ipaddress.md) to either a device or VM interface.
+
+Object types extend Django's stock `ContentType` model with two additional attributes that NetBox uses to reason about model capabilities: `public` (whether the model is intended for direct reference) and `features` (the set of NetBox model features the model supports, such as change logging or custom fields).
+
+!!! note "For plugin authors"
+    NetBox code should generally use `ObjectType.objects.get_for_model()` rather than Django's `ContentType.objects.get_for_model()`, so that the resulting object exposes NetBox's `public` and `features` attributes. The two managers are otherwise interchangeable.
+
+## Fields
+
+### App Label
+
+The Django application label to which the model belongs (e.g. `dcim`, `ipam`, or a plugin's app label).
+
+### Model
+
+The lowercase model name (e.g. `device`, `prefix`).
+
+### Public
+
+Indicates whether the model is part of NetBox's public data model. Public models are those intended to be referenced from other objects (e.g. via custom fields or generic relations). Internal models — those backing implementation details — are non-public and are excluded from interfaces that expose model selection to end users.
+
+### Features
+
+The list of NetBox model features the underlying model supports (for example: `change_logging`, `custom_fields`, `tags`, `webhooks`). This list is consulted when filtering object types for a particular feature, e.g. when populating the model selector for an event rule.

+ 3 - 3
docs/models/dcim/cablebundle.md

@@ -1,8 +1,8 @@
 # Cable Bundles
 
-A cable bundle is a logical grouping of individual [cables](./cable.md). Bundles can be used to organize cables that share a common purpose, route, or physical grouping (such as a conduit or harness).
+A cable bundle is a logical grouping of individual [cables](./cable.md). Bundles are useful for organizing cables that share a common purpose, route, or physical grouping such as a conduit, trunk, or wiring harness.
 
-Assigning cables to a bundle is optional and does not affect cable tracing or connectivity. Bundles persist independently of their member cables: deleting a cable clears its bundle assignment but does not delete the bundle itself.
+Assigning cables to a bundle is optional and does not affect cable tracing or connectivity. Bundles persist independently of their member cables: deleting a cable clears its bundle assignment but does not delete the bundle itself, allowing the bundle to be reused for replacement cables.
 
 ## Fields
 
@@ -12,4 +12,4 @@ A unique name for the cable bundle.
 
 ### Description
 
-A brief description of the bundle's purpose or contents.
+An optional short description of the bundle's purpose or contents.

+ 17 - 3
docs/models/dcim/macaddress.md

@@ -1,11 +1,25 @@
 # MAC Addresses
 
-A MAC address object in NetBox comprises a single Ethernet link layer address, and represents a MAC address as reported by or assigned to a network interface. MAC addresses can be assigned to [device](../dcim/device.md) and [virtual machine](../virtualization/virtualmachine.md) interfaces. A MAC address can be specified as the primary MAC address for a given device or VM interface.
+A MAC address object in NetBox represents a single Ethernet link-layer address as reported by or assigned to a network interface. MAC addresses can be assigned to [device interfaces](./interface.md) and [virtual machine interfaces](../virtualization/vminterface.md), and any one of an interface's assigned MAC addresses may be designated as its **primary** MAC address.
 
-Most interfaces have only a single MAC address, hard-coded at the factory. However, on some devices (particularly virtual interfaces) it is possible to assign additional MAC addresses or change existing ones. For this reason NetBox allows multiple MACAddress objects to be assigned to a single interface.
+Most physical interfaces have only a single MAC address, hard-coded at the factory. However, some interfaces (particularly virtual interfaces and modular hardware) support multiple or reassignable MAC addresses. To accommodate this, NetBox models MAC addresses as first-class objects which may be created, modified, and reassigned independently of any specific interface.
 
 ## Fields
 
 ### MAC Address
 
-The 48-bit MAC address, in colon-hexadecimal notation (e.g. `aa:bb:cc:11:22:33`).
+The 48-bit MAC address, expressed in colon-hexadecimal notation (for example, `aa:bb:cc:11:22:33`).
+
+### Assigned Object
+
+A generic reference to the [device interface](./interface.md) or [virtual machine interface](../virtualization/vminterface.md) to which this MAC address is assigned. A MAC address may exist without being assigned to any interface.
+
+A MAC address that is currently designated as the primary MAC of its parent interface cannot be reassigned to (or unassigned from) another interface without first clearing the primary designation.
+
+### Description
+
+An optional human-readable description of the MAC address.
+
+### Comments
+
+Free-form Markdown-supported notes regarding the MAC address.

+ 16 - 4
docs/models/dcim/moduletypeprofile.md

@@ -1,8 +1,8 @@
 # Module Type Profiles
 
-Each [module type](./moduletype.md) may optionally be assigned a profile according to its classification. A profile can extend module types with user-configured attributes. For example, you might want to specify the input current and voltage of a power supply, or the clock speed and number of cores for a processor.
+Each [module type](./moduletype.md) may optionally be assigned a profile according to its classification. A profile can extend module types with user-configured attributes — for example, the input current and voltage of a power supply, or the clock speed and number of cores for a processor.
 
-Module type attributes are managed via the configuration of a [JSON schema](https://json-schema.org/) on the profile. For example, the following schema introduces three module type attributes, two of which are designated as required attributes.
+Module type attributes are managed by configuring a [JSON schema](https://json-schema.org/) on the profile. The schema below introduces three module type attributes, two of which are designated as required:
 
 ```json
 {
@@ -29,10 +29,22 @@ Module type attributes are managed via the configuration of a [JSON schema](http
 }
 ```
 
-The assignment of module types to a profile is optional. The designation of a schema for a profile is also optional: A profile can be used simply as a mechanism for classifying module types if the addition of custom attributes is not needed.
+Both the assignment of module types to a profile and the designation of a schema for a profile are optional: a profile can be used purely as a classification mechanism if the addition of custom attributes is not needed.
 
 ## Fields
 
+### Name
+
+A unique name for the profile (for example, `Power Supply` or `Disk`).
+
+### Description
+
+An optional description of the profile.
+
 ### Schema
 
-This field holds the [JSON schema](https://json-schema.org/) for the profile. The configured JSON schema must be valid (or the field must be null).
+An optional [JSON schema](https://json-schema.org/) defining the attributes that may (or must) be set on each assigned module type. The schema must be valid JSON Schema, or else null.
+
+### Comments
+
+Free-form Markdown-supported notes about the profile.

+ 17 - 3
docs/models/extras/configcontextprofile.md

@@ -1,6 +1,6 @@
 # Config Context Profiles
 
-Profiles can be used to organize [configuration contexts](./configcontext.md) and to enforce a desired structure for their data. The later is achieved by defining a [JSON schema](https://json-schema.org/) to which all config context with this profile assigned must comply.
+Profiles can be used to organize [configuration contexts](./configcontext.md) and to enforce a desired structure for their data. The latter is achieved by defining a [JSON schema](https://json-schema.org/) to which all config contexts assigned to the profile must comply. Any context whose data fails validation against the profile's schema cannot be saved.
 
 For example, the following schema defines two keys, `size` and `priority`, of which the former is required:
 
@@ -22,12 +22,26 @@ For example, the following schema defines two keys, `size` and `priority`, of wh
 }
 ```
 
+A profile's schema may also be populated from a [data source](../core/datasource.md), enabling the schema to be maintained externally (for example, in a git repository) and synchronized into NetBox.
+
 ## Fields
 
 ### Name
 
-A unique human-friendly name.
+A unique, human-friendly name for the profile.
+
+### Description
+
+An optional description of the profile's purpose.
 
 ### Schema
 
-The JSON schema to be enforced for all assigned config contexts (optional).
+An optional [JSON schema](https://json-schema.org/) document. When set, the schema is enforced for every config context assigned to this profile. Leaving the schema blank allows the profile to be used purely as an organizational grouping.
+
+### Data Source / Data File / Data Path
+
+Optional pointers to a remote [data source](../core/datasource.md) and file from which the schema is populated. When configured, the schema field is overwritten on synchronization.
+
+### Auto Sync Enabled
+
+When set, the profile's schema is automatically refreshed whenever the upstream data file is updated.

+ 19 - 0
docs/models/users/group.md

@@ -0,0 +1,19 @@
+# Groups
+
+A group is a collection of [users](./user.md) which share a common set of permissions. Assigning [object permissions](./objectpermission.md) to a group, rather than to individual users, simplifies the administration of permissions for related users (e.g. members of a particular team).
+
+A user inherits the union of all permissions assigned to each group of which they are a member, in addition to any permissions assigned directly to the user.
+
+## Fields
+
+### Name
+
+A unique name for the group.
+
+### Description
+
+A short description of the group's role or membership.
+
+### Object Permissions
+
+The set of [object permissions](./objectpermission.md) granted to all members of this group.

+ 50 - 0
docs/models/users/objectpermission.md

@@ -0,0 +1,50 @@
+# Object Permissions
+
+An object permission grants the ability to perform one or more actions (e.g. view, add, change, delete) against a defined set of object types, and may be restricted to a subset of objects matching a configured filter. Permissions are assigned to [users](./user.md) and/or [groups](./group.md); a user's effective permissions are the union of those assigned directly and those inherited via group membership.
+
+See the [permissions documentation](../../administration/permissions.md) for a detailed walkthrough of how permissions are evaluated.
+
+## Fields
+
+### Name
+
+A short, human-readable name for the permission.
+
+### Description
+
+An optional longer description of what the permission grants.
+
+### Enabled
+
+When unset, the permission is effectively disabled: it remains assigned to its users and groups, but is ignored during permission checks. This is useful for temporarily revoking access without altering assignments.
+
+### Object Types
+
+The list of NetBox model types to which this permission applies (e.g. `dcim.device`, `ipam.prefix`).
+
+### Actions
+
+The list of actions granted by the permission. The standard CRUD actions are `view`, `add`, `change`, and `delete`. Models may also register custom actions (e.g. `napalm` on `dcim.device`); custom actions appear here when supported by the selected object types.
+
+### Constraints
+
+An optional [Django ORM-style filter](https://docs.djangoproject.com/en/stable/topics/db/queries/#field-lookups) expressed as JSON. When set, the permission applies only to objects matching the filter. Multiple constraint sets may be supplied as a JSON list; an object matches if it satisfies any of the sets (logical OR).
+
+For example, to grant a permission only over devices in a specific site:
+
+```json
+{"site__slug": "ny-dc1"}
+```
+
+Or, to apply the permission to devices in either of two sites:
+
+```json
+[
+    {"site__slug": "ny-dc1"},
+    {"site__slug": "sj-dc2"}
+]
+```
+
+### Users & Groups
+
+The [users](./user.md) and [groups](./group.md) to which this permission is assigned.

+ 6 - 2
docs/models/users/ownergroup.md

@@ -1,9 +1,13 @@
 # Owner Groups
 
-Groups are used to correlate and organize [owners](./owner.md). The assignment of an owner to a group has no bearing on the relationship of owned objects to their owners.
+Groups are used to correlate and organize [owners](./owner.md). The assignment of an owner to a group has no bearing on the relationship of owned objects to their owners; groups exist purely as an organizational convenience for administrators.
 
 ## Fields
 
 ### Name
 
-The name of the group.
+A unique name for the group.
+
+### Description
+
+An optional description of the group's role or membership.

+ 51 - 0
docs/models/users/token.md

@@ -0,0 +1,51 @@
+# Tokens
+
+A token is a secret credential associated with a [user](./user.md) which authenticates requests to NetBox's REST and GraphQL APIs. A user may hold multiple tokens; each can be independently expired, restricted, or revoked.
+
+Beginning with NetBox v4.5, two token versions are supported. v2 tokens (the default for newly-created tokens) are stored only as a salted HMAC digest, and the plaintext is shown to the user only once at creation time. Legacy v1 tokens store the plaintext directly and are retained for backward compatibility; their use is discouraged. See the [REST API authentication](../../integrations/rest-api.md#authentication) documentation for the request header formats used by each version.
+
+## Fields
+
+### Version
+
+Indicates whether this is a v1 (legacy) or v2 token. v2 is the default and is strongly preferred.
+
+### User
+
+The [user](./user.md) which owns the token. All requests authenticated with the token are performed as this user.
+
+### Description
+
+A free-form description of the token (e.g. naming the application or automation that uses it).
+
+### Created
+
+The date and time at which the token was created.
+
+### Expires
+
+An optional date and time after which the token will no longer be valid. Tokens without an expiration never expire.
+
+### Last Used
+
+The date and time at which the token was most recently used to authenticate a request. This value is updated at most once per minute to limit database write overhead.
+
+### Enabled
+
+When unset, the token is temporarily revoked. Disabled tokens cannot be used to authenticate requests but are not deleted, allowing them to be re-enabled later.
+
+### Write Enabled
+
+When unset, the token may only be used for read operations (e.g. `GET`). All write operations (`POST`, `PATCH`, `PUT`, `DELETE`) made with the token will be rejected.
+
+### Allowed IPs
+
+An optional list of IPv4 and/or IPv6 prefixes from which the token may be used. If set, requests originating from any other source address will be rejected.
+
+### Key (v2 only)
+
+A short, randomly-generated identifier transmitted in plaintext alongside each request. The key allows the server to locate the matching token record before validating the secret portion.
+
+### Plaintext (v1 only)
+
+The full plaintext value of a v1 token. Stored as-is in the database, which is one of the reasons v2 tokens are preferred.

+ 47 - 0
docs/models/users/user.md

@@ -0,0 +1,47 @@
+# Users
+
+A user represents an individual account in NetBox. Users authenticate to access the application, and may be granted permissions either directly or through their assigned [groups](./group.md). Each user can hold one or more API [tokens](./token.md) for use with the REST and GraphQL APIs.
+
+NetBox extends Django's stock user model to support multiple API tokens per user, configurable [object permissions](./objectpermission.md), and integration with [remote authentication backends](../../administration/authentication/overview.md).
+
+## Fields
+
+### Username
+
+A unique identifier used to log in. May contain letters, digits, and the characters `@ . + - _`. Username comparison is case-insensitive: a new user cannot be created whose username differs from an existing one only in letter case.
+
+### First Name
+
+The user's given name. Optional.
+
+### Last Name
+
+The user's family name. Optional.
+
+### Email Address
+
+The user's email address. Used by NetBox to send notifications (e.g. error reports) when configured to do so.
+
+### Active
+
+When unset, the user is treated as inactive and may not log in. Disabling a user is generally preferable to deletion, as it preserves the user's history in change records and other related objects.
+
+### Staff Status
+
+Designates whether the user can log into the (legacy) Django admin site. Most NetBox functionality is exposed via the standard UI; staff status is rarely needed.
+
+### Superuser Status
+
+Designates that the user is granted all permissions implicitly, bypassing all permission checks. Use sparingly.
+
+### Date Joined
+
+The date and time at which the user account was created.
+
+### Groups
+
+The set of [groups](./group.md) to which the user belongs. A user inherits all permissions assigned to each of their groups.
+
+### Object Permissions
+
+The set of [object permissions](./objectpermission.md) assigned directly to the user, in addition to those granted via group membership.

+ 2 - 1
docs/plugins/development/index.md

@@ -116,9 +116,10 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
 | `middleware`          | A list of middleware classes to append after NetBox's build-in middleware                                                          |
 | `queues`              | A list of custom background task queues to create                                                                                  |
 | `events_pipeline`     | A list of handlers to add to [`EVENTS_PIPELINE`](../../configuration/miscellaneous.md#events_pipeline), identified by dotted paths |
-| `search_extensions`   | The dotted path to the list of search index classes (default: `search.indexes`)                                                    |
+| `search_indexes`      | The dotted path to the list of search index classes (default: `search.indexes`)                                                    |
 | `data_backends`       | The dotted path to the list of data source backend classes (default: `data_backends.backends`)                                     |
 | `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`)                        |
+| `menu`                | The dotted path to a top-level navigation menu provided by the plugin (default: `navigation.menu`)                                 |
 | `menu_items`          | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`)                                |
 | `graphql_schema`      | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`)                                           |
 | `user_preferences`    | The dotted path to the dictionary mapping of user preferences defined by the plugin (default: `preferences.preferences`)           |

+ 56 - 9
docs/plugins/development/permissions.md

@@ -1,24 +1,71 @@
 # Custom Model Actions
 
-Plugins can register custom permission actions for their models. These actions appear as checkboxes in the ObjectPermission form, making it easy for administrators to grant or restrict access to plugin-specific functionality without manually entering action names.
+Plugins can register custom permission actions for their models. These actions appear as checkboxes in the [ObjectPermission](../../models/users/objectpermission.md) form alongside the standard view/add/change/delete actions, making it easy for administrators to grant or restrict access to plugin-specific functionality without manually entering action names.
 
-For example, a plugin might define a "sync" action for a model that syncs data from an external source, or a "bypass" action that allows users to bypass certain restrictions.
+For example, a plugin might define a `sync` action for a model that fetches data from an external source, or a `bypass` action that allows users to skip certain restrictions.
 
 ## Registering Model Actions
 
-The preferred way to register custom actions is via Django's `Meta.permissions` on the model class. NetBox will automatically register these as model actions when the app is loaded:
+The preferred way to register custom actions is via Django's `Meta.permissions` on the model class. NetBox automatically registers these as model actions when the app is loaded:
 
-```python
+```python title="models.py"
 from netbox.models import NetBoxModel
 
-class MyModel(NetBoxModel):
-    # ...
+class WidgetSync(NetBoxModel):
+    # ... fields ...
 
     class Meta:
         permissions = [
-            ('sync', 'Synchronize data from external source'),
-            ('export', 'Export data to external system'),
+            ('sync', 'Synchronize widgets from external source'),
+            ('export', 'Export widgets to external system'),
         ]
 ```
 
-Once registered, these actions appear as checkboxes in a flat list when creating or editing an ObjectPermission.
+Once registered, these actions appear as checkboxes in a flat list when creating or editing an ObjectPermission. The first element of each tuple is the action's identifier (used in code) and the second is the help text shown to administrators in the UI.
+
+!!! note "Reserved action names"
+    Action names that conflict with NetBox's built-in CRUD verbs (`view`, `add`, `change`, `delete`) are reserved and cannot be reused as custom actions.
+
+## Granting an Action
+
+Custom actions are granted just like any standard permission:
+
+1. Open **Admin → Object Permissions** and create a new permission.
+2. Select the relevant object type(s) (e.g. `my_plugin | widget sync`).
+3. Tick the custom action's checkbox (e.g. `sync`).
+4. Assign the permission to the desired users and/or groups.
+
+Optional [constraints](../../models/users/objectpermission.md#constraints) may be added to limit the permission to a subset of objects.
+
+## Checking an Action at Runtime
+
+Custom actions follow Django's standard permission naming convention `<app_label>.<action>_<model>`. To check whether the current user is authorized to perform a custom action against a model, call `user.has_perm()`:
+
+```python
+if request.user.has_perm('my_plugin.sync_widgetsync'):
+    # User is permitted to invoke the sync action
+    ...
+```
+
+Per-object permission checks (which respect any [constraints](../../models/users/objectpermission.md#constraints) on the granting permission) work the same way:
+
+```python
+if request.user.has_perm('my_plugin.sync_widgetsync', obj=widget):
+    ...
+```
+
+For class-based views, NetBox provides `ObjectPermissionRequiredMixin` from `utilities.views`, which integrates cleanly with these custom actions:
+
+```python title="views.py"
+from utilities.views import ObjectPermissionRequiredMixin
+from django.views.generic import View
+
+class SyncWidgetView(ObjectPermissionRequiredMixin, View):
+    queryset = WidgetSync.objects.all()
+    permission_required = 'my_plugin.sync_widgetsync'
+
+    def post(self, request, pk):
+        widget = self.get_object()
+        widget.sync()
+        return redirect(widget.get_absolute_url())
+```

+ 6 - 0
mkdocs.yml

@@ -189,6 +189,8 @@ nav:
             - DataFile: 'models/core/datafile.md'
             - DataSource: 'models/core/datasource.md'
             - Job: 'models/core/job.md'
+            - ObjectChange: 'models/core/objectchange.md'
+            - ObjectType: 'models/core/objecttype.md'
         - DCIM:
             - Cable: 'models/dcim/cable.md'
             - CableBundle: 'models/dcim/cablebundle.md'
@@ -280,8 +282,12 @@ nav:
             - Tenant: 'models/tenancy/tenant.md'
             - TenantGroup: 'models/tenancy/tenantgroup.md'
         - Users:
+            - Group: 'models/users/group.md'
+            - ObjectPermission: 'models/users/objectpermission.md'
             - Owner: 'models/users/owner.md'
             - OwnerGroup: 'models/users/ownergroup.md'
+            - Token: 'models/users/token.md'
+            - User: 'models/users/user.md'
         - Virtualization:
             - Cluster: 'models/virtualization/cluster.md'
             - ClusterGroup: 'models/virtualization/clustergroup.md'

+ 1 - 1
netbox/netbox/configuration_example.py

@@ -168,7 +168,7 @@ LOGGING = {}
 LOGIN_PERSISTENCE = False
 
 # The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
-# re-authenticate. (Default: 1209600 [14 days])
+# re-authenticate. If set to None (the default), Django's SESSION_COOKIE_AGE is used (two weeks).
 LOGIN_TIMEOUT = None
 
 # Hide the login form. Useful when only allowing SSO authentication.