[
  {
    "path": "CHANGELOG.md",
    "content": "[//]: # \"START/v1.8.0\"\n\n# v1.8.0\nThis release includes improvements for fetching files with Connect, like support for fetching a file referenced in multiple items and a better error response if a file is not synced yet.\n\n- [IMPROVED] Connect now returns an improved error response when you try to fetch a file that isn't synced in your Connect server's local storage. {4378}\n\n- [FIXED] Connect now allows you to fetch a file if the file is referenced in multiple items across different vaults. {4262}\n- [FIXED] Connect file storage management can now handle multiple items referencing the same file. {4263}\n\n[//]: # \"START/v1.7.3\"\n\n# v1.7.3\nThis release updates the dependencies and the images used to build Connect.\n\n- [IMPROVED] Update go version to 1.22.4. {4146}\n\n- [SECURITY] Upgraded to Go 1.21.x and Debian 12 Bookworm for building and CI. {3870}\n- [SECURITY] Bump google.golang.org/grpc to address a vulnerable dependency alert. {3855}\n\n[//]: # \"START/v1.7.2\"\n\n# v1.7.2\nThis release contains bug fixes.\n\n- [FIXED] Connect can update Document items. {3709}\n- [FIXED] The first request sent to a new Connect server should not respond with a '401 invalid bearer token' error. {3711}\n\n[//]: # \"START/v1.7.1\"\n\n# v1.7.1\nThis release contains security and performance improvements.\n- [IMPROVED] Connect item usage flushing has been optimised to decrease network traffic to the 1Password servers. {2600}\n\n- [SECURITY] Addressed bug where PATCH could be abused to read items using a write only token. Credit to Carlos Baraza. {3392}\n\n\n[//]: # \"START/v1.7.0\"\n\n# v1.7.0\nThis release improves an error message and fixes two bugs.\n- [IMPROVED] Creating a new DOCUMENT item now correctly returns an error message that this is not supported. {3127}\n\n- [FIXED] Connect should no longer sporadically return an \"database is locked\" error when synchronising with the 1Password servers. {3381}\n- [FIXED] Requests to /v1/vaults/{vaultUUID}/items no longer causes \"Invalid Item UUID\" to be logged. {3254}\n\n\n[//]: # \"START/v1.6.1\"\n\n# v1.6.1\nThis release addresses memory leak issues for Connect.\n- [FIXED] Addressed a problem that caused Connect's memory consumption to increase with about 100B for every request handled. {2842}\n- [FIXED] Addressed a problem that could cause Connect's memory consumption to increase by more than 1KB per request served after one of the containers (connect-api or connect-sync) disconnected. {3362}\n\n[//]: # \"START/v1.6.0\"\n\n# v1.6.0\nThis release moves Connect to a distroless base image.\n- [SECURITY] Connect Docker images  are now based on gcr.io/distroless/base to reduce the number of false positives when scanned with container security tools. {3296}\n\n[//]: # \"START/v1.5.8\"\n\n# v1.5.8\nThis release contains two security improvements.\n- [SECURITY] Addressed an issue that allows an attacker with network access to the Connect server to allocate high amounts of memory on the Connect server if it is configured to serve connections over TLS. We inherited this issue from the Go standard library, where the issue has been assigned vulnerability identifier CVE-2022-41717. {3274}\n- [SECURITY] Update Debian packages for the Docker release images. {3195}\n\n[//]: # \"START/v1.5.7\"\n\n# v1.5.7\nSecurity updates, and a fix to allow Connect to (re)start if the 1Password server is unavailable.\n- [FIXED] The API container will now continue to serve requests if it is restarted when 1Password server in unreachable. {2560}\n\n- [SECURITY] Update connect images to use latest base image. {2738}\n- [SECURITY] Update Debian packages for the Docker release images. {2694}\n\n[//]: # \"START/v1.5.6\"\n\n# v1.5.6\nThis version addresses bugs related to file objects\n\n- [FIXED] The file objects now have the correct ID and name. {2625, 2616}\n- [FIXED] Categories route will no longer return status 500 when no error is present. {2170}\n\n[//]: # \"START/v1.5.5\"\n\n# v1.5.5\nThis version addresses a few bugs and a security improvement.\n\n- [FIXED] Connect now properly returns the `content_path` attribute of the File object. {2598}\n\n- [SECURITY] Updated Debian packages for the Docker release images. {2516}\n\n[//]: # \"START/v1.5.4\"\n\n# v1.5.4\nThis release updates the API spec of Connect to mention some supported fields and addresses two bugs.\n\n- [IMPROVED] The API spec now mentions the \"label\" field for autofill URL's. {2446}\n- [IMPROVED] The API spec now mentions support for the item categories \"MEDICAL_RECORD\" and \"SSH_KEY\". {2468}\n\n- [FIXED] Connect accepts JSON inputs with camelCase keys as used in the SDK's again. {2317}\n- [FIXED] Connect no longer return an error mentioning the files directory in some cases when a file is uploaded to a vault that Connect syncs. {2498,2539}\n\n- [SECURITY] The Docker images are now using an updated version of the Debian base images. {2516}\n\n[//]: # \"START/v1.5.3\"\n\n# v1.5.3\nThis release contains improvements in validation during the SRP process.\n\n- [SECURITY] Improved validation of server parameters in SRP process. Credits to Cure53. {2442}\n\n[//]: # \"START/v1.5.2\"\n\n# v1.5.2\nThis release addressed a few minor bugs and a memory leak.\n\n- [IMPROVED] Connect now validates `monthYear` and `date` field types. {2075}\n\n- [FIXED] Addressed a problem that could let the memory consumption of the sync container slowly increase for every incoming request under certain circumstances. {2017}\n- [FIXED] Having a single quote as a filter value no longer throws a stack trace and now returns a 400. {1442}\n\n[//]: # \"START/v1.5.1\"\n\n# v1.5.1\nThis release addresses a few problems related to Connect's synchronization mechanism.\n\n- [IMPROVED] Connect stays better in sync with 1Password when it experiences connection problems. {2150}\n\n- [FIXED] Fixed synchronization of templates that could prevent Connect from starting after changing its credentials. {2151}\n- [FIXED] Templates should now correctly synchronize to Connect after emptying Connect's database. {2151}\n- [FIXED] Connect should no longer return a \"failed to initialize database\" error when upgrading from Connect v1.3.x. {2005}\n\n[//]: # \"START/v1.5.0\"\n\n# v1.5.0\nThis release introduces Docker images for the `arm64` and `arm/v7` platforms.\n\nFurthermore, the bus between the api and sync container can now be manually configured for environments that cannot run containers with the `NET_BROADCAST` capability.\n\n- [NEW] Bus auto-discovery can now be disabled by explicitly defining peers with the `$OP_BUS_PEERS` environment variable. {1778}\n- [NEW] Connect API and Connect Sync Docker images for arm64/v8 and arm/v7 architectures are now available. {1771}\n\n- [FIXED] The label field is now always correctly set (instead of being empty) for fields that have a USERNAME or PASSWORD purpose. {1657}\n\n\n\n[//]: # \"START/v1.4.0\"\n\n# v1.4.0\nThis release introduces enhancements for OTP fields, generated passwords and item usage histories.\n\nThis update includes non-breaking changes to the  [OpenAPI spec](docs/openapi/spec.yaml).\n\n- [NEW] Specific characters can now be excluded from generated passwords by setting `excludeCharacters` in the recipe. {1631}\n- [IMPROVED] Added the generated TOTP code to the body of OTP fields in Items. {1557}\n- [IMPROVED] Connect now records more detailed item usage histories. {14198}\n\n\n[//]: # \"START/v1.3.0\"\n\n# v1.3.0\n\nThis release introduces support for accessing documents and files attached to items.\n\n## Features\n\n- [NEW] Add support for getting files through the API. Details can be found in the [OpenAPI spec](docs/openapi/spec.yaml).\n\n- [IMPROVED] Item modification requests are now rejected by the Connect API if the authenticated account is in the frozen billing state. {1175}\n- [IMPROVED] Items containing one or more file fields can now be updated through Connect. {1386}\n- [IMPROVED] API spec now defines valid vault and item IDs. {1399}\n- [IMPROVED] Connect API now reports its version number in the `1Password-Connect-Version` header. {1443}\n\n\n## Fixed\n\n- [FIXED] Resolved an issue that prevented servers from authenticating with accounts after an admin updated the sign-in url. {1446}\n\n\n[//]: # \"START/v1.2.0\"\n\n# v1.2.0\n\nThis release contains several improvements to securing you API requests and better error handling when dealing with Items.\n\n## Features\n\n- [NEW] Add TLS for the Connect API. Provide your own certificates or use LetsEncrypt\n\n- [IMPROVED] A more user-friendly error is returned when the user attempts to create an item with the \"CUSTOM\" category. {1382}\n\n## Fixed\n\n- [FIXED] Resolved issue where API could incorrectly report a successful item deletion. {1368}\n- [FIXED] Fixed the handling of TOTP fields for items in Connect {1372}\n\n[//]: # \"START/v1.1.1\"\n\n# v1.1.1\n\nThis release contains improvements to account synchronization and token validation.\n\n[//]: # \"START/v1.1.0\"\n\n# v1.1.0\n\nThis release contains several improvements to configuring, running, and troubleshooting a Secrets Automation deployment in your environment.\n\nThe health endpoint returns additional information about the status of the local item database and the \"DELETE\" endpoint now permanently deletes items instead of moving them to the Trash.\n\n## Features\n\n- [NEW] Updated the Connect API to delete items instead of trashing them. {11613}\n- [NEW] Connect can now also be unlocked by sending a bearer token to the /health endpoint. {1305}\n- [NEW] The /health endpoint reports the status of the data in the local database under account_data. {1305}\n- [NEW] Added support for new API Credential item category. {1344}\n\n- [IMPROVED] The `OP_LOG_LEVEL` evironment variable can now be used to customize the logging level of the Connect server and the sync component. {1205}\n- [IMPROVED] The API request timeout for waiting for a healthy sync is now per-request and can be changed with the `OP_SYNC_TIMEOUT` environment variable. {1305}\n- [IMPROVED] Item usage is now reported to 1password.com in batches at most once a second to avoid being rate limited. {1331}\n- [IMPROVED] Syncer applies an exponential backoff when retrying if it cannot find a valid credential. {1341}\n\n## Fixes\n\n- [FIXED] Corrected several minor mistakes in the OpenAPI spec. {1326}\n- [FIXED] Log error when a connection problem causes the failure of a Connect request. {1302}\n- [FIXED] Updating the password for a Login or Password Item correctly updates the Password History. {1350}\n\n[//]: # \"START/v1.0.0\"\n\n# v1.0.0\n\nThe 1Password Connect server provides applications and services with information from 1Password as part of 1Password Secrets Automation.\n"
  },
  {
    "path": "README.md",
    "content": "<img src=\"https://github.com/1Password/connect/assets/46452606/0f7cf2a8-a290-41fc-b78d-3dfb1017f9be\" alt=\"\" role=\"img\" >\n\n\n<div align=\"center\">\n  <h1>1Password Connect</h1>\n  <p>Access your 1Password secrets using 1Password Connect</p>\n  <a href=\"https://developer.1password.com/docs/connect/get-started\">\n    <img alt=\"Get started\" src=\"https://user-images.githubusercontent.com/45081667/226940040-16d3684b-60f4-4d95-adb2-5757a8f1bc15.png\" height=\"37\"/>\n  </a>\n</div>\n\n---\n\nWith 1Password Connect, you can securely access your 1Password items and vaults in your company's apps and cloud infrastructure using a private REST API or the 1Password CLI.\n## ✨ Get started\nCheck out our [developer documentation](https://developer.1password.com/docs/connect/get-started) to get started. Or see our deployment examples [here](https://github.com/1Password/connect/tree/main/examples).\n## 💙 Community & Support\n\n- File an [issue](https://github.com/1Password/connect/issues) for bugs and feature requests.\n- Join the [Developer Slack workspace](https://developer.1password.com/joinslack)\n- Subscribe to the [Developer Newsletter](https://1password.com/dev-subscribe/)\n\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "# Connect Server Configuration\n\nThe Connect server consists of 2 containers, `1password/connect-api` and `1password/connect-sync` running in the same network. The two containers require a shared volume to store an encrpyted copy of your data.\n\n## Data Volume\n\n- `/home/opuser/.op/data`: The default location of the shared volume\n\n**Note**: If you use are setting the `XDG_DATA_HOME` environment variable to a path other than `/home/opuser` you will need to mount your data volume at that path as well.\n\n## Environment variables\n\nThe following environment variable configuration options are available for both contains:\n\n- `OP_SESSION`: path to the 1password-credentials.json file\n- `OP_HTTP_PORT`: port used by the HTTP server\n- `OP_LOG_LEVEL`: set the logging level of the container\n- `XDG_DATA_HOME`: set the path where the `.op/data` directory should be created\n- `OP_BUS_PORT`: the port used for listening to incoming bus connections from other containers (by default, this is a random free port)\n- `OP_BUS_PEERS`: a comma-separated listed of `[hostname]:[bus port]` pairs of other containers to connect to (see _Manual bus configuration_ for more details)\n\nAll other configuration options are only relevant for the `1password/connect-api` container:\n\n- `OP_HTTPS_PORT`: port used by the HTTP sever when TLS is configured (see below)\n- `OP_SYNC_TIMEOUT`: define how long to wait for initial sync to complete\n\nWhen using TLS with own certificate:\n\n- `OP_TLS_KEY_FILE`: path to the private key file.\n- `OP_TLS_CERT_FILE`: path to the certificate file. This should be the full certificate chain.\n\nWhen using TLS with Let's Encrypt:\n\n- `OP_TLS_USE_LETSENCRYPT`: should be set to any value.\n- `OP_TLS_DOMAIN`: the (sub-)domain for which to request a certificate. The DNS-records for this domain must point to the Connect server.\n\n## Manual bus configuration (v1.5.0 and above)\nBy default, the 2 containers automatically discover and connect to the shared bus. This discovery mechanism requires the `NET_BROADCAST` capability, which cannot always be granted to containers.\n\nIt is also possible to manually configure the shared bus:\n1. Assign a static port for the bus by setting the `OP_BUS_PORT` environment variable to a free port for all containers.\n2. Set the `OP_BUS_PEERS` environment variable for the api container to `[hostname]:[bus port]`, where hostname is the name of the sync container and bus port the value of `OP_BUS_PORT` for the sync container.\n3. Set the `OP_BUS_PEERS` environment variable for the sync container to `[hostname]:[bus port]`, where hostname is the name of the api container and bus port the value of `OP_BUS_PORT` for the api container.\n\nFor example, if the containers are called `op-connect-api` and `op-connect-sync`. The configuration of `op-connect-api` would be:\n```\nOP_BUS_PORT=11223\nOP_BUS_PEERS=op-connect-sync:11223\n```\nAnd for `op-connect-sync`:\n```\nOP_BUS_PORT=11223\nOP_BUS_PEERS=op-connect-api:11223\n```\n"
  },
  {
    "path": "docs/openapi/spec.yaml",
    "content": "openapi: \"3.0.2\"\ninfo:\n  title: 1Password Connect\n  description: >-\n    REST API interface for 1Password Connect.\n  version: \"1.7.1\"\n  contact:\n    name: 1Password Integrations\n    email: support@1password.com\n    url: https://support.1password.com/\nservers:\n  - url: http://localhost:8080/v1\n\ntags:\n  - name: Items\n    description: Access and manage items inside 1Password Vaults\n  - name: Vaults\n    description: Access 1Password Vaults\n  - name: Activity\n    description: Access API Request Activity\n\ncomponents:\n  securitySchemes:\n    ConnectToken:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n  schemas:\n    ErrorResponse:\n      type: object\n      properties:\n        status:\n          type: integer\n          description: HTTP Status Code\n        message:\n          type: string\n          description: A message detailing the error\n    File:\n      type: object\n      properties:\n        id:\n          type: string\n          description: ID of the file\n        name:\n          type: string\n          description: Name of the file\n        size:\n          type: integer\n          description: Size in bytes of the file\n        content_path:\n          type: string\n          description: Path of the Connect API that can be used to download the contents of this file.\n          readOnly: true\n        section:\n          type: object\n          description: For files that are in a section, this field describes the section.\n          properties:\n            id:\n              type: string\n        content:\n          type: string\n          format: byte\n          description: Base64-encoded contents of the file. Only set if size <= OP_MAX_INLINE_FILE_SIZE_KB kb and `inline_files` is set to `true`.\n      example:\n        id: 6r65pjq33banznomn7q22sj44e\n        name: foo.txt\n        size: 35\n        content_path: v1/vaults/ionaiwtdvgclrixbt6ztpqcxnq/items/p7eflcy7f5mk7vg6zrzf5rjjyu/files/6r65pjq33banznomn7q22sj44e/content\n        content: VGhlIGZ1dHVyZSBiZWxvbmdzIHRvIHRoZSBjdXJpb3VzLgo=\n    Vault:\n      type: object\n      properties:\n        id:\n          type: string\n          pattern: '^[\\da-z]{26}$'\n        name:\n          type: string\n        description:\n          type: string\n        attributeVersion:\n          type: integer\n          description: The vault version\n        contentVersion:\n          type: integer\n          description: The version of the vault contents\n        items:\n          description: Number of active items in the vault\n          type: integer\n        type:\n          type: string\n          enum:\n            - USER_CREATED\n            - PERSONAL\n            - EVERYONE\n            - TRANSFER\n        createdAt:\n          type: string\n          format: date-time\n          readOnly: true\n        updatedAt:\n          type: string\n          format: date-time\n          readOnly: true\n    GeneratorRecipe:\n      description: The recipe is used in conjunction with the \"generate\" property to set the character set used to generate a new secure value\n      type: object\n      properties:\n        length:\n          type: integer\n          description: Length of the generated value\n          default: 32\n          minimum: 1\n          maximum: 64\n        characterSets:\n          type: array\n          items:\n            type: string\n            enum:\n              - LETTERS\n              - DIGITS\n              - SYMBOLS\n          minimum: 0\n          maximum: 3\n          uniqueItems: true\n        excludeCharacters:\n          type: string\n          description: \"List of all characters that should be excluded from generated passwords.\"\n          example: \"abc1\"\n    Item:\n      type: object\n      required:\n        - vault\n        - category\n      properties:\n        id:\n          type: string\n          pattern: '^[\\da-z]{26}$'\n        title:\n          type: string\n        vault:\n          type: object\n          required:\n            - id\n          properties:\n            id:\n              type: string\n              pattern: '^[\\da-z]{26}$'\n        category:\n          type: string\n          enum:\n            - \"LOGIN\"\n            - \"PASSWORD\"\n            - \"API_CREDENTIAL\"\n            - \"SERVER\"\n            - \"DATABASE\"\n            - \"CREDIT_CARD\"\n            - \"MEMBERSHIP\"\n            - \"PASSPORT\"\n            - \"SOFTWARE_LICENSE\"\n            - \"OUTDOOR_LICENSE\"\n            - \"SECURE_NOTE\"\n            - \"WIRELESS_ROUTER\"\n            - \"BANK_ACCOUNT\"\n            - \"DRIVER_LICENSE\"\n            - \"IDENTITY\"\n            - \"REWARD_PROGRAM\"\n            - \"DOCUMENT\"\n            - \"EMAIL_ACCOUNT\"\n            - \"SOCIAL_SECURITY_NUMBER\"\n            - \"MEDICAL_RECORD\"\n            - \"SSH_KEY\"\n            - \"CUSTOM\"\n        urls:\n          type: array\n          items:\n            type: object\n            required:\n              - href\n            properties:\n              label:\n                type: string\n              primary:\n                type: boolean\n              href:\n                type: string\n                format: url\n          example:\n            - primary: true\n              href: https://example.com\n            - href: https://example.org\n        favorite:\n          type: boolean\n          default: false\n        tags:\n          type: array\n          items:\n            type: string\n        version:\n          type: integer\n        state:\n          type: string\n          readOnly: true\n          enum:\n            - \"ARCHIVED\"\n            - \"DELETED\"\n        createdAt:\n          type: string\n          format: date-time\n          readOnly: true\n        updatedAt:\n          type: string\n          format: date-time\n          readOnly: true\n        lastEditedBy:\n          type: string\n          readOnly: true\n    FullItem:\n      allOf:\n        - $ref: \"#/components/schemas/Item\"\n        - type: object\n          properties:\n            sections:\n              type: array\n              items:\n                type: object\n                properties:\n                  id:\n                    type: string\n                  label:\n                    type: string\n            fields:\n              type: array\n              items:\n                $ref: '#/components/schemas/Field'\n            files:\n              type: array\n              items:\n                $ref: '#/components/schemas/File'\n    Field:\n      type: object\n      required:\n        - id\n        - type\n      properties:\n        id:\n          type: string\n        section:\n          type: object\n          properties:\n            id:\n              type: string\n        type:\n          type: string\n          default: \"STRING\"\n          enum:\n            - \"STRING\"\n            - \"EMAIL\"\n            - \"CONCEALED\"\n            - \"URL\"\n            - \"TOTP\"\n            - \"DATE\"\n            - \"MONTH_YEAR\"\n            - \"MENU\"\n        purpose:\n          description: Some item types, Login and Password, have fields used for autofill. This property indicates that purpose and is required for some item types.\n          type: string\n          enum:\n            - \"\"\n            - \"USERNAME\"\n            - \"PASSWORD\"\n            - \"NOTES\"\n        label:\n          type: string\n        value:\n          type: string\n        generate:\n          description: If value is not present then a new value should be generated for this field\n          type: boolean\n          default: false\n        recipe:\n          $ref: \"#/components/schemas/GeneratorRecipe\"\n        entropy:\n          description: For fields with a purpose of `PASSWORD` this is the entropy of the value\n          type: number\n          readOnly: true\n    APIRequest:\n      description: Represents a request that was made to the API. Including what Token was used and what resource was accessed.\n      type: object\n      properties:\n        requestId:\n          description: The unique id used to identify a single request.\n          type: string\n          format: uuid\n        timestamp:\n          description: The time at which the request was processed by the server.\n          type: string\n          format: date-time\n          readOnly: true\n        action:\n          type: string\n          enum:\n            - READ\n            - CREATE\n            - UPDATE\n            - DELETE\n        result:\n          type: string\n          enum:\n            - SUCCESS\n            - DENY\n        actor:\n          type: object\n          properties:\n            id:\n              type: string\n              format: uuid\n            account:\n              type: string\n            jti:\n              type: string\n            userAgent:\n              type: string\n            requestIp:\n              type: string\n        resource:\n          type: object\n          properties:\n            type:\n              type: string\n              enum:\n                - ITEM\n                - VAULT\n            vault:\n              type: object\n              properties:\n                id:\n                  type: string\n                  pattern: '^[\\da-z]{26}$'\n            item:\n              type: object\n              properties:\n                id:\n                  type: string\n                  pattern: '^[\\da-z]{26}$'\n            itemVersion:\n              type: integer\n    Patch:\n      type: array\n      items:\n        type: object\n        properties:\n          op:\n            type: string\n            enum: [ add, remove, replace ]\n          path:\n            type: string\n            description: An RFC6901 JSON Pointer pointing to the Item document, an Item Attribute, and Item Field by Field ID, or an Item Field Attribute\n            example: \"/fields/06gnn2b95example10q91512p5/label\"\n          value:\n            type: object\n        required:\n          - op\n          - path\n    ServiceDependency:\n      description: The state of a registered server dependency.\n      type: object\n      properties:\n        service:\n          type: string\n        status:\n          type: string\n        message:\n          type: string\n          description: Human-readable message for explaining the current state.\n\n\npaths:\n  /activity:\n    get:\n      operationId: GetApiActivity\n      tags:\n        - Activity\n      summary: Retrieve a list of API Requests that have been made.\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: query\n          name: limit\n          schema:\n            type: integer\n            example: 10\n            default: 50\n          description: How many API Events should be retrieved in a single request.\n        - in: query\n          name: offset\n          schema:\n            type: integer\n            example: 50\n            default: 0\n          description: How far into the collection of API Events should the response start\n      responses:\n        \"200\":\n          description: OK\n          headers:\n            Content-Range:\n              description: An decription of what part of the collection has been returned as well as the total size.\n              schema:\n                type: string\n                example: 1-50/1134\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: \"#/components/schemas/APIRequest\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n  /vaults:\n    get:\n      operationId: GetVaults\n      tags:\n        - Vaults\n      summary: Get all Vaults\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: query\n          name: filter\n          schema:\n            type: string\n            example: name eq \"Some Vault Name\"\n          description: Filter the Vault collection based on Vault name using SCIM eq filter\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: \"#/components/schemas/Vault\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n  /vaults/{vaultUuid}:\n    get:\n      operationId: GetVaultById\n      tags:\n        - Vaults\n      summary: Get Vault details and metadata\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Vault to fetch Items from\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Vault\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: Vault not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 404\n                message: vault {itemUuid} not found\n  /vaults/{vaultUuid}/items:\n    get:\n      operationId: GetVaultItems\n      tags:\n        - Items\n      summary: Get all items for inside a Vault\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Vault to fetch Items from\n        - in: query\n          name: filter\n          schema:\n            type: string\n            example: title eq \"Some Item Name\"\n          description: Filter the Item collection based on Item name using SCIM eq filter\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: \"#/components/schemas/Item\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"404\":\n          description: Vault not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 404\n                message: vault {vaultUuid} not found\n    post:\n      operationId: CreateVaultItem\n      tags:\n        - Items\n      summary: Create a new Item\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Vault to create an Item in\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/FullItem\"\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/FullItem\"\n        \"400\":\n          description: Unable to create item due to invalid input\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 400\n                message: Invalid item category\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: Item not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n  /vaults/{vaultUuid}/items/{itemUuid}:\n    get:\n      operationId: GetVaultItemById\n      tags:\n        - Items\n      summary: Get the details of an Item\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Vault to fetch Item from\n        - in: path\n          name: itemUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Item to fetch\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/FullItem\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: Item not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                itemNotFound:\n                  summary: Item not found\n                  value:\n                    status: 404\n                    message: item {itemUuid} not found\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n    put:\n      operationId: UpdateVaultItem\n      tags:\n        - Items\n      summary: Update an Item\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Item's Vault\n        - in: path\n          name: itemUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Item to update\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/FullItem\"\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/FullItem\"\n        \"400\":\n          description: Unable to create item due to invalid input\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 400\n                message: The item doesn't have a {example field name} field.\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: Item not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                itemNotFound:\n                  summary: Item not found\n                  value:\n                    status: 404\n                    message: item {itemUuid} not found\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n    delete:\n      operationId: DeleteVaultItem\n      tags:\n        - Items\n      summary: Delete an Item\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Vault the item is in\n        - in: path\n          name: itemUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Item to update\n      responses:\n        \"204\":\n          description: Successfully deleted an item\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: Item not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n    patch:\n      description: >\n        Applies a modified [RFC6902 JSON Patch](https://tools.ietf.org/html/rfc6902) document to an Item or ItemField. This endpoint only supports `add`, `remove` and `replace` operations.\n\n\n        When modifying a specific ItemField, the ItemField's ID in the `path` attribute of the operation object: `/fields/{fieldId}`\n\n      operationId: PatchVaultItem\n      tags:\n        - Items\n      summary: Update a subset of Item attributes\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Vault the item is in\n        - in: path\n          name: itemUuid\n          schema:\n            type: string\n            pattern: '^[\\da-z]{26}$'\n          required: true\n          description: The UUID of the Item to update\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/Patch\"\n            examples:\n              ReplaceAllAttributes:\n                value:\n                  - op: replace\n                    path: \"/\"\n                    value: { \"title\": \"New Title\", \"favorite\": true, \"tags\": [ \"tag1\", \"tag2\" ], \"...\": \"Any attr from FullItem schema\" }\n                summary: Replace an entire Item with new fields. Equivalent to a PUT request.\n              PatchItemAttr:\n                value:\n                  - op: \"replace\"\n                    path: \"/favorite\"\n                    value: true\n                  - op: \"remove\"\n                    path: \"/tags/1\"\n                summary: Update specific Item attributes\n              PatchItemField:\n                value:\n                  - op: \"add\"\n                    path: \"/fields\"\n                    value: { \"label\": \"New Field\", \"type\": \"string\", \"value\": \"hunter2\" }\n                summary: Add a new ItemField to the Item\n              PatchItemFieldWithID:\n                value:\n                  - op: \"replace\"\n                    path: \"/fields/r9qxq7xnhfhukoxsc8ymqr0y11\"\n                    value: { \"label\": \"Replacement Title\", \"type\": \"string\", \"value\": \"new value\" }\n                  - op: \"remove\"\n                    path: \"/fields/h2nl155dshi043yse7wa3u1hs7\"\n                summary: Modify or remove an ItemField.\n              PatchItemFieldAttr:\n                value:\n                  - op: \"add\"\n                    path: \"/fields/s2ju540zlna8bdj4uro7sj64rk/label\"\n                    value: \"New field name\"\n                  - op: \"remove\"\n                    path: \"/fields/s2ju540zlna8bdj4uro7sj64rk/value\"\n                summary: Modify a specific ItemField attribute.\n\n      responses:\n        \"200\":\n          description: OK - Item updated. If no Patch operations were provided, Item is unmodified.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/FullItem\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: Item not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                itemNotFound:\n                  summary: Item not found\n                  value:\n                    status: 404\n                    message: item {itemUuid} not found\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n  /vaults/{vaultUuid}/items/{itemUuid}/files:\n    get:\n      operationId: GetItemFiles\n      tags:\n        - Files\n      summary: Get all the files inside an Item\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            format: uuid\n          required: true\n          description: The UUID of the Vault to fetch Items from\n        - in: path\n          name: itemUuid\n          schema:\n            type: string\n            format: uuid\n          required: true\n          description: The UUID of the Item to fetch files from\n        - in: query\n          name: inline_files\n          schema:\n            type: boolean\n            example: true\n          description: Tells server to return the base64-encoded file contents in the response.\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: \"#/components/schemas/File\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"404\":\n          description: Item not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                itemNotFound:\n                  summary: Item not found\n                  value:\n                    status: 404\n                    message: item {itemUuid} not found\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n        \"413\":\n          description: File content too large to display\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                fileTooLarge:\n                  summary: File too large\n                  value:\n                    status: 413\n                    message: File is too large to inline in request. Use the /v1/vaults/{vaultUUID}/items/{itemUUID}/files/{fileUUID}/content endpoint instead.\n  /vaults/{vaultUuid}/items/{itemUuid}/files/{fileUuid}:\n    get:\n      operationId: GetDetailsOfFileById\n      tags:\n        - Files\n      summary: Get the details of a File\n      security:\n        - ConnectToken: [ ]\n      parameters:\n        - in: path\n          name: vaultUuid\n          schema:\n            type: string\n            format: uuid\n          required: true\n          description: The UUID of the Vault to fetch Item from\n        - in: path\n          name: itemUuid\n          schema:\n            type: string\n            format: uuid\n          required: true\n          description: The UUID of the Item to fetch File from\n        - in: path\n          name: fileUuid\n          schema:\n            type: string\n            format: uuid\n          required: true\n          description: The UUID of the File to fetch\n        - in: query\n          name: inline_files\n          schema:\n            type: boolean\n            example: true\n          description: Tells server to return the base64-encoded file contents in the response.\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/File\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"403\":\n          description: Unauthorized access\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 403\n                message: vault {vaultUuid} is not in scope\n        \"404\":\n          description: File not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                fileNotFound:\n                  summary: File not found\n                  value:\n                    status: 404\n                    message: file {fileUuid} not found\n                itemNotFound:\n                  summary: Item not found\n                  value:\n                    status: 404\n                    message: item {itemUuid} not found\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n        \"413\":\n          description: File content too large to display\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                fileTooLarge:\n                  summary: File too large\n                  value:\n                    status: 413\n                    message: File is too large to inline in request. Use the /v1/vaults/{vaultUUID}/items/{itemUUID}/files/{fileUUID}/content endpoint instead.\n  /vaults/{vaultUuid}/items/{itemUuid}/files/{fileUuid}/content:\n    parameters:\n      - in: path\n        name: vaultUuid\n        schema:\n          type: string\n          format: uuid\n        required: true\n        description: The UUID of the Vault the item is in\n      - in: path\n        name: itemUuid\n        schema:\n          type: string\n          format: uuid\n        required: true\n        description: The UUID of the Item the File is in\n      - in: path\n        name: fileUuid\n        required: true\n        schema:\n          type: string\n        description: UUID of the file to get content from\n    get:\n      operationId: DownloadFileByID\n      tags:\n        - Files\n      summary: Get the content of a File\n      security:\n        - ConnectToken: [ ]\n      responses:\n        \"200\":\n          description: \"Success\"\n          content:\n            application/octet-stream:\n              schema:\n                type: string\n                format: binary\n          headers:\n            Content-Disposition:\n              schema:\n                type: string\n                example: attachment; filename=\"privkey.pem\"\n            Content-Length:\n              schema:\n                type: string\n                example: \"6432\"\n        \"401\":\n          description: Invalid or missing token\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              example:\n                status: 401\n                message: Invalid token signature\n        \"404\":\n          description: File not found\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ErrorResponse\"\n              examples:\n                fileNotFound:\n                  summary: File not found\n                  value:\n                    status: 404\n                    message: file {fileUuid} not found\n                itemNotFound:\n                  summary: Item not found\n                  value:\n                    status: 404\n                    message: item {itemUuid} not found\n                vaultNotFound:\n                  summary: Vault not found\n                  value:\n                    status: 404\n                    message: vault {vaultUuid} not found\n  /heartbeat:\n    get:\n      operationId: GetHeartbeat\n      tags:\n        - Health\n      summary: Ping the server for liveness\n      servers:\n        - url: http://localhost:8080\n      responses:\n        \"200\":\n          description: OK\n          content:\n            text/plain:\n              schema:\n                type: string\n                example: .\n  /health:\n    get:\n      operationId: GetServerHealth\n      tags:\n        - Health\n      summary: Get state of the server and its dependencies.\n      servers:\n        - url: http://localhost:8080\n      responses:\n        \"200\":\n          description: OK\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [ \"name\", \"version\" ]\n                properties:\n                  name:\n                    type: string\n                  version:\n                    type: string\n                    description: The Connect server's version\n                  dependencies:\n                    type: array\n                    items:\n                      $ref: \"#/components/schemas/ServiceDependency\"\n              examples:\n                WaitingForAPIRequest:\n                  value:\n                    name: 1Password Connect API\n                    version: 1.2.1\n                    dependencies:\n                      - service: sync\n                        status: \"TOKEN_NEEDED\"\n                      - service: sqlite\n                        status: \"ACTIVE\"\n                        message: \"Connected to./1password.sqlite\"\n                  summary: API server waiting for first authenticated request\n\n  /metrics:\n    get:\n      operationId: GetPrometheusMetrics\n      tags:\n        - Metrics\n      description: See Prometheus documentation for a complete data model.\n      summary: Query server for exposed Prometheus metrics\n      servers:\n        - url: http://localhost:8080\n      responses:\n        \"200\":\n          description: Successfully returned Prometheus metrics\n          content:\n            text/plain:\n              schema:\n                type: string\n                example: |\n                  # HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.\n                  # TYPE go_gc_duration_seconds summary\n                  go_gc_duration_seconds{quantile=\"0\"} 2.9153e-05\n                  go_gc_duration_seconds{quantile=\"0.25\"} 6.2832e-05\n                  go_gc_duration_seconds{quantile=\"0.5\"} 9.7187e-05\n                  go_gc_duration_seconds{quantile=\"0.75\"} 0.000112967\n                  go_gc_duration_seconds{quantile=\"1\"} 0.000215819\n                  go_gc_duration_seconds_sum 0.001376862\n                  go_gc_duration_seconds_count 14\n"
  },
  {
    "path": "examples/aws-ecs-fargate/README.md",
    "content": "# AWS ECS with Fargate\n\nThe CloudFormation document in this folder will create the following resources, along with the necessary rules, groups, and policies to make them work.\n\nThe result is a publicly accessible hostname (via HTTPS) that routes requests to a 1Password Connect instance running in AWS Fargate. You can use your own VPC and subnets, or optionally set up DNS records automatically with Route 53. \n\nTo customize further, like deploying into an existing ECS cluster, use this CloudFormation as a starting point and tweak it with a text editor or Amazon's CloudFormation Designer tool.\n\n## Networking Resources\n\n- An ACM certificate for HTTPS\n- An ALB (Application Load Balancer) with HTTPS\n- A VPC (optional, if you don’t provide your own)\n- 2 Public subnets (optional, if you don’t provide your own)\n- An Internet Gateway (optional, if creating a VPC)\n- Route 53 DNS records (optional, if you provide a hosted zone ID)\n\n## ECS Resources\n\n- An ECS Cluster\n- Task Definition\n  - `1password/connect-api` container\n  - `1password/connect-sync` container\n- Service Discovery for internal routing\n\n## Getting Started\n\nWhen [importing this CloudFormation template](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-console-create-stack-template.html), you’ll be prompted to provide the following:\n\n- **CredentialsJson**: The plain JSON contents of your `1password-credentials.json` file.  The template will encode this to base64 and store it securely in AWS Secrets Manager.\n- **DomainName**: A fully qualified domain name for the ALB (e.g., `connect.example.com`). This is required for HTTPS.\n- **Route53HostedZoneID** (optional): If you’re using Route 53, provide the hosted zone ID to automatically create DNS records. Leave blank to set up DNS manually with your registrar.\n- **VPCID**, **PublicSubnets** (optional): Provide these if you want to use an existing VPC and subnets. Leave as default to create a new VPC. \n- **VPCCIDR** (optional): Sets the CIDR for a new VPC if you’re creating one; ignored if using an existing VPC.\n\n### DNS Setup (If Not Using Route 53)\nIf you leave `Route53HostedZoneID` blank, the deployment will pause at the `HTTPSCertificate` creation step, waiting for DNS validation:\n1. Check the ACM console for the certificate (status: `Pending validation`).\n2. Add the provided CNAME record to your DNS registrar to validate the certificate.\n3. Wait for the certificate to be issued (status: `Issued`), then the deployment will continue.\n4. After deployment, use the `ExternalUrl` output (or `CNAME Name` and `CNAME Value`) to create an `A` (with alias) or `CNAME` record in your registrar to point to the ALB.\n\nFor more details, see the deployment note in the template description."
  },
  {
    "path": "examples/aws-ecs-fargate/connect-server.yaml",
    "content": "AWSTemplateFormatVersion: 2010-09-09\nDescription: >-\n  Sets up 1Password Connect on Fargate with an ALB for HTTPS ingress, in a public VPC subnet.\n  Note: If you leave Route53HostedZoneID blank, deployment will pause at HTTPSCertificate creation.\n  Check the ACM console for a CNAME record to add to your DNS registrar for validation, then wait for the certificate to be issued.\n\n# Makes the params look nice in the AWS console\nMetadata:\n  AWS::CloudFormation::Interface:\n    ParameterGroups:\n      - Label:\n          default: Network & Credentials Setup\n        Parameters:\n          - VPCID\n          - VPCCIDR\n          - PublicSubnets\n          - Route53HostedZoneID\n          - DomainName\n          - CredentialsJson\n    ParameterLabels:\n      VPCID:\n        default: VPC ID\n      VPCCIDR:\n        default: VPC CIDR\n      PublicSubnets:\n        default: Public Subnets\n      Route53HostedZoneID:\n        default: Route 53 Hosted Zone\n      DomainName:\n        default: 1Password Connect Server domain name\n      CredentialsJson:\n        default: 1Password Credentials JSON\n\n# Stuff you can tweak when you deploy\nParameters:\n  VPCID:\n    Type: String\n    Description: Got a VPC ID? Toss it here, or leave it blank to make a new one\n    Default: \"\"\n  VPCCIDR:\n    Type: String\n    Description: CIDR for a new VPC if we're making one\n    Default: 10.0.0.0/16\n  PublicSubnets:\n    Type: CommaDelimitedList\n    Description: List of public subnet IDs, need at least 2 if you're using an existing VPC\n    Default: \"\"\n  Route53HostedZoneID:\n    Type: String\n    Description: Route 53 hosted zone ID for creating DNS records (leave empty to set up manually)\n    Default: \"\"\n  DomainName:\n    Type: String\n    Description: Domain name for the ALB (e.g., connect.example.com), must be in Route 53 if hosted zone is provided\n    MinLength: 1\n    ConstraintDescription: Need to provide a domain name for HTTPS\n  CredentialsJson:\n    Type: String\n    Description: Your 1password-credentials.json as plain JSON\n    NoEcho: true\n    MinLength: 1\n    ConstraintDescription: gotta have something here\n\n# Check if we need to whip up a new VPC or create DNS records\nConditions:\n  CreateVPC: !Equals [!Ref VPCID, \"\"]\n  CreateRoute53Records: !Not [!Equals [!Ref Route53HostedZoneID, \"\"]]\n  OutputDNSRecordContent: !Not [Condition: CreateRoute53Records]\n\n# Predefined CIDRs for the VPC and subnets\nMappings:\n  SubnetConfig:\n    VPC:\n      CIDR: '10.0.0.0/16'\n    PublicOne:\n      CIDR: '10.0.1.0/24'\n    PublicTwo:\n      CIDR: '10.0.2.0/24'\n\nResources:\n  # Secret to store the base64-encoded credentials\n  CredentialsSecret:\n    Type: AWS::SecretsManager::Secret\n    Properties:\n      Name: !Sub 'CredentialsSecret-${AWS::StackName}'\n      SecretString: !Base64 { \"Fn::Sub\": \"${CredentialsJson}\" }\n\n  # VPC stuff if we need to make one\n  VPC:\n    Condition: CreateVPC\n    Type: AWS::EC2::VPC\n    Properties:\n      EnableDnsSupport: true\n      EnableDnsHostnames: true\n      CidrBlock: !Ref VPCCIDR\n  PublicSubnetOne: # first subnet for the VPC\n    Condition: CreateVPC\n    Type: AWS::EC2::Subnet\n    Properties:\n      AvailabilityZone: !Select [0, !GetAZs '']\n      VpcId: !Ref VPC\n      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']\n      MapPublicIpOnLaunch: true\n  PublicSubnetTwo: # second subnet for the VPC\n    Condition: CreateVPC\n    Type: AWS::EC2::Subnet\n    Properties:\n      AvailabilityZone: !Select [1, !GetAZs '']\n      VpcId: !Ref VPC\n      CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']\n      MapPublicIpOnLaunch: true\n  InternetGateway: # need this for internet access in the new VPC\n    Condition: CreateVPC\n    Type: AWS::EC2::InternetGateway\n  GatewayAttachement:\n    Condition: CreateVPC\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n  PublicRouteTable:\n    Condition: CreateVPC\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n  PublicRoute: # sets up the route to the internet\n    Condition: CreateVPC\n    DependsOn: GatewayAttachement\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      DestinationCidrBlock: '0.0.0.0/0'\n      GatewayId: !Ref InternetGateway\n  PublicSubnetOneRouteTableAssociation:\n    Condition: CreateVPC\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetOne\n      RouteTableId: !Ref PublicRouteTable\n  PublicSubnetTwoRouteTableAssociation:\n    Condition: CreateVPC\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetTwo\n      RouteTableId: !Ref PublicRouteTable\n\n  # ECS cluster to run stuff\n  ECSCluster:\n    Type: AWS::ECS::Cluster\n\n  # SGs for Fargate and ALB\n  LoadBalancerSG:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: Access to the public facing load balancer\n      VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 443\n          ToPort: 443\n          CidrIp: 0.0.0.0/0\n          Description: HTTPS from anywhere\n      SecurityGroupEgress:\n        - IpProtocol: tcp\n          FromPort: 8080\n          ToPort: 8080\n          CidrIp: !If\n            - CreateVPC\n            - !GetAtt VPC.CidrBlock\n            - 0.0.0.0/0\n  FargateContainerSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: Access to the Fargate containers\n      VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n  EcsSecurityGroupIngressFromPublicALB:\n    Type: AWS::EC2::SecurityGroupIngress\n    Properties:\n      Description: Ingress from the public ALB\n      GroupId: !Ref FargateContainerSecurityGroup\n      IpProtocol: tcp\n      FromPort: 8080\n      ToPort: 8080\n      SourceSecurityGroupId: !Ref LoadBalancerSG\n  EcsSecurityGroupIngressFromSelf:\n    Type: AWS::EC2::SecurityGroupIngress\n    Properties:\n      Description: Ingress for sync container communication\n      GroupId: !Ref FargateContainerSecurityGroup\n      IpProtocol: tcp\n      FromPort: 8081\n      ToPort: 8081\n      SourceSecurityGroupId: !Ref FargateContainerSecurityGroup\n  EcsSecurityGroupEgress:\n    Type: AWS::EC2::SecurityGroupEgress\n    Properties:\n      GroupId: !Ref FargateContainerSecurityGroup\n      IpProtocol: tcp\n      FromPort: 443\n      ToPort: 443\n      CidrIp: 0.0.0.0/0\n      Description: HTTPS to external services (e.g., Docker Hub)\n\n  # ALB setup\n  LoadBalancer:\n    Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n    Properties:\n      Scheme: internet-facing\n      LoadBalancerAttributes:\n        - Key: idle_timeout.timeout_seconds\n          Value: '30'\n      Subnets: !If\n        - CreateVPC\n        - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]\n        - !Ref PublicSubnets\n      SecurityGroups: [!Ref LoadBalancerSG]\n\n  HTTPSCertificate:\n    Type: AWS::CertificateManager::Certificate\n    Properties:\n      DomainName: !Ref DomainName\n      ValidationMethod: DNS\n      DomainValidationOptions: !If\n        - CreateRoute53Records\n        - - DomainName: !Ref DomainName\n            HostedZoneId: !Ref Route53HostedZoneID\n        - !Ref AWS::NoValue\n\n  HTTPSListener:\n    DependsOn:\n      - LoadBalancer\n      - HTTPSCertificate\n    Type: AWS::ElasticLoadBalancingV2::Listener\n    Properties:\n      DefaultActions:\n        - TargetGroupArn: !Ref ConnectTargetGroup\n          Type: forward\n      LoadBalancerArn: !Ref LoadBalancer\n      Port: 443\n      Protocol: HTTPS\n      SslPolicy: ELBSecurityPolicy-TLS13-1-2-Res-2021-06\n      Certificates:\n        - CertificateArn: !Ref HTTPSCertificate\n\n  ConnectTargetGroup:\n    Type: AWS::ElasticLoadBalancingV2::TargetGroup\n    DependsOn:\n      - LoadBalancer\n    Properties:\n      HealthCheckIntervalSeconds: 6\n      HealthCheckPath: /heartbeat\n      HealthCheckProtocol: HTTP\n      HealthCheckTimeoutSeconds: 5\n      HealthyThresholdCount: 2\n      TargetType: ip\n      Name: connect\n      Port: 8080\n      Protocol: HTTP\n      UnhealthyThresholdCount: 2\n      VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n      TargetGroupAttributes:\n        - Key: deregistration_delay.timeout_seconds\n          Value: 60\n\n  # DNS record for the ALB (if using Route 53)\n  DNSRecord:\n    Condition: CreateRoute53Records\n    Type: AWS::Route53::RecordSet\n    Properties:\n      HostedZoneId: !Ref Route53HostedZoneID\n      Comment: DNS record pointing to load balancer for 1Password Connect\n      Name: !Ref DomainName\n      Type: A\n      AliasTarget:\n        DNSName: !GetAtt LoadBalancer.DNSName\n        HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID\n\n  # Service discovery for internal routing\n  ServiceDiscoveryNamespace:\n    Type: AWS::ServiceDiscovery::PrivateDnsNamespace\n    Properties:\n      Description: Private DNS namespace for 1Password Connect\n      Vpc: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n      Name: onepassword\n\n  ConnectServiceDiscovery:\n    Type: AWS::ServiceDiscovery::Service\n    DependsOn:\n      - ServiceDiscoveryNamespace\n    Properties:\n      DnsConfig:\n        DnsRecords:\n          - TTL: 60\n            Type: SRV\n      Name: connect\n      NamespaceId: !Ref ServiceDiscoveryNamespace\n      HealthCheckCustomConfig:\n        FailureThreshold: 1\n\n  # IAM roles for ECS\n  ECSRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: '2012-10-17'\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: ecs-tasks.amazonaws.com\n            Action: sts:AssumeRole\n      Policies:\n        - PolicyName: ECSFargatePermissions\n          PolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n              - Effect: Allow\n                Action:\n                  - ec2:AttachNetworkInterface\n                  - ec2:CreateNetworkInterface\n                  - ec2:DeleteNetworkInterface\n                  - ec2:DescribeNetworkInterfaces\n                  - ec2:DetachNetworkInterface\n                  - elasticloadbalancing:RegisterTargets\n                  - elasticloadbalancing:DeregisterTargets\n                  - elasticloadbalancing:DescribeTargetHealth\n                Resource: '*'\n\n  ECSTaskExecutionRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: '2012-10-17'\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: ecs-tasks.amazonaws.com\n            Action: sts:AssumeRole\n      Policies:\n        - PolicyName: AmazonECSTaskExecutionRolePolicy\n          PolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n              - Effect: Allow\n                Action:\n                  - ecr:GetAuthorizationToken\n                  - ecr:BatchCheckLayerAvailability\n                  - ecr:GetDownloadUrlForLayer\n                  - ecr:BatchGetImage\n                  - logs:CreateLogStream\n                  - logs:PutLogEvents\n                Resource: '*'\n        - PolicyName: SecretAccess\n          PolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n              - Effect: Allow\n                Action:\n                  - secretsmanager:GetSecretValue\n                Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:CredentialsSecret-${AWS::StackName}*'\n\n  # Logs in CloudWatch\n  CloudWatchLogsGroup:\n    Type: AWS::Logs::LogGroup\n    Properties:\n      LogGroupName: onepassword-connect\n      RetentionInDays: 30\n\n  # Task def for the Connect containers\n  TaskDefinition:\n    Type: AWS::ECS::TaskDefinition\n    DependsOn:\n      - ECSTaskExecutionRole\n      - CloudWatchLogsGroup\n    Properties:\n      Cpu: 256\n      Memory: 512\n      NetworkMode: awsvpc\n      RequiresCompatibilities:\n        - FARGATE\n      ExecutionRoleArn: !Ref ECSTaskExecutionRole\n      TaskRoleArn: !Ref ECSRole\n      Volumes:\n        - Name: connect-data\n      ContainerDefinitions:\n        - Name: connect-api\n          Image: 1password/connect-api:latest\n          PortMappings:\n            - ContainerPort: 8080\n              Protocol: tcp\n          MountPoints:\n            - ContainerPath: /home/opuser/.op/data\n              SourceVolume: connect-data\n          Secrets:\n            - Name: OP_SESSION\n              ValueFrom: !Ref CredentialsSecret\n          LogConfiguration:\n            LogDriver: awslogs\n            Options:\n              awslogs-group: !Ref CloudWatchLogsGroup\n              awslogs-region: !Ref AWS::Region\n              awslogs-stream-prefix: connect-api\n        - Name: connect-sync\n          Image: 1password/connect-sync:latest\n          MountPoints:\n            - ContainerPath: /home/opuser/.op/data\n              SourceVolume: connect-data\n          Environment:\n            - Name: OP_HTTP_PORT\n              Value: '8081'\n          Secrets:\n            - Name: OP_SESSION\n              ValueFrom: !Ref CredentialsSecret\n          LogConfiguration:\n            LogDriver: awslogs\n            Options:\n              awslogs-group: !Ref CloudWatchLogsGroup\n              awslogs-region: !Ref AWS::Region\n              awslogs-stream-prefix: connect-sync\n\n  # ECS service to run the containers\n  ConnectService:\n    Type: AWS::ECS::Service\n    DependsOn:\n      - LoadBalancer\n      - HTTPSListener\n    Properties:\n      ServiceName: connect\n      Cluster: !Ref ECSCluster\n      LaunchType: FARGATE\n      NetworkConfiguration:\n        AwsvpcConfiguration:\n          AssignPublicIp: ENABLED\n          SecurityGroups:\n            - !Ref FargateContainerSecurityGroup\n          Subnets: !If\n            - CreateVPC\n            - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]\n            - !Ref PublicSubnets\n      TaskDefinition: !Ref TaskDefinition\n      LoadBalancers:\n        - ContainerName: connect-api\n          ContainerPort: 8080\n          TargetGroupArn: !Ref ConnectTargetGroup\n      ServiceRegistries:\n        - ContainerName: connect-api\n          ContainerPort: 8080\n          RegistryArn: !GetAtt ConnectServiceDiscovery.Arn\n\n# Stuff you'll need after deployment\nOutputs:\n  CNAMEName:\n    Condition: OutputDNSRecordContent\n    Description: Name of a DNS record to point to the load balancer (use with CNAME Value for manual setup)\n    Value: !Sub ${DomainName}.\n  CNAMEValue:\n    Condition: OutputDNSRecordContent\n    Description: Value of a DNS record to point to the load balancer (use with CNAME Name for manual setup)\n    Value: !Sub ${LoadBalancer.DNSName}.\n  ExternalUrl:\n    Description: The URL of the external load balancer\n    Value: !Join ['', ['https://', !GetAtt LoadBalancer.DNSName]]\n    Export:\n      Name: !Join [':', [!Ref AWS::StackName, 'ExternalUrl']]\n      "
  },
  {
    "path": "examples/beta/aws-ecs-apigateway-cfn/README.md",
    "content": "# AWS ECS with Fargate and API Gateway\n\nThe CloudFormation document in this folder will create the following resources, along with the necessary rules, groups, and policies to make them work.\n\nThe result is a publicly accessible hostname (via HTTPS) that routes requests to a 1Password Connect instance running in AWS Fargate, using API Gateway for secure ingress. You can use your own VPC and subnets.\n\nTo customize further, like deploying into an existing ECS cluster, use this CloudFormation as a starting point and tweak it with a text editor or Amazon's CloudFormation Designer tool.\n\n## Networking Resources\n\n- A VPC Link for API Gateway to route to the Fargate service\n- An API Gateway with HTTPS\n- A VPC (optional, if you don’t provide your own)\n- 2 Public subnets (optional, if you don’t provide your own)\n- An Internet Gateway (optional, if creating a VPC)\n\n\n## ECS Resources\n\n- An ECS Cluster\n- Task Definition\n  - `1password/connect-api` container\n  - `1password/connect-sync` container\n- Service Discovery for internal routing\n\n## Getting Started\n\nWhen [importing this CloudFormation template](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-console-create-stack-template.html), you’ll be prompted to provide the following:\n\n- **CredentialsJson**: The plain JSON contents of your `1password-credentials.json` file (e.g., `{\"token\":\"your-token\"}`). The template will encode this to base64 and store it securely in AWS Secrets Manager.\n- **VPCID**, **PublicSubnets** (optional): Provide these if you want to use an existing VPC and subnets. Leave as default to create a new VPC.\n- **VPCCIDR** (optional): Sets the CIDR for a new VPC if you’re creating one; ignored if using an existing VPC.\n\nFor more details, see the deployment note in the template description."
  },
  {
    "path": "examples/beta/aws-ecs-apigateway-cfn/connect-server.yaml",
    "content": "AWSTemplateFormatVersion: 2010-09-09\nDescription: Sets up 1Password Connect on Fargate with API Gateway, stores 1password-credentials.json in Secrets Manager, and makes a VPC if you don’t give one\n\n# Makes the params look nice in the AWS console\nMetadata:\n  AWS::CloudFormation::Interface:\n    ParameterGroups:\n      - Label:\n          default: Network & Credentials Setup\n        Parameters:\n          - VPCID\n          - VPCCIDR\n          - PublicSubnets\n          - CredentialsJson\n    ParameterLabels:\n      CredentialsJson:\n        default: 1Password Credentials JSON\n      VPCID:\n        default: VPC ID\n      VPCCIDR:\n        default: VPC CIDR\n      PublicSubnets:\n        default: Public Subnets\n\n# Parameters you can tweak when you deploy\nParameters:\n  VPCID:\n    Type: String\n    Description: Got a VPC ID? Toss it here, or leave it blank to make a new one\n    Default: \"\"\n  VPCCIDR:\n    Type: String\n    Description: CIDR for a new VPC if we're making one\n    Default: 10.0.0.0/16\n  PublicSubnets:\n    Type: CommaDelimitedList\n    Description: List of public subnet IDs, need at least 2 if you're using an existing VPC\n    Default: \"\"\n  CredentialsJson:\n    Type: String\n    Description: Your 1password-credentials.json as plain JSON\n    NoEcho: true\n    MinLength: 1\n    ConstraintDescription: gotta have something here\n\n# Check if we need to whip up a new VPC\nConditions:\n  CreateVPC:\n    Fn::Equals:\n      - !Ref VPCID\n      - \"\"\n\n# Predefined CIDRs for the VPC and subnets\nMappings:\n  SubnetConfig:\n    VPC:\n      CIDR: '10.0.0.0/16'\n    PublicOne:\n      CIDR: '10.0.1.0/24'\n    PublicTwo:\n      CIDR: '10.0.2.0/24'\n\nResources:\n  # Secret to store the base64-encoded credentials\n  CredentialsSecret:\n    Type: AWS::SecretsManager::Secret\n    Properties:\n      Name: !Sub 'CredentialsSecret-${AWS::StackName}'\n      SecretString: !Base64 { \"Fn::Sub\": \"${CredentialsJson}\" }\n\n  # VPC stuff if we need to make one\n  VPC:\n    Condition: CreateVPC\n    Type: AWS::EC2::VPC\n    Properties:\n      CidrBlock: !Ref VPCCIDR\n      EnableDnsSupport: true\n      EnableDnsHostnames: true\n\n  PublicSubnetOne:\n    Condition: CreateVPC\n    Type: AWS::EC2::Subnet\n    Properties:\n      AvailabilityZone:\n        Fn::Select:\n          - 0\n          - Fn::GetAZs: ''\n      VpcId: !Ref VPC\n      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']\n      MapPublicIpOnLaunch: true\n\n  PublicSubnetTwo:\n    Condition: CreateVPC\n    Type: AWS::EC2::Subnet\n    Properties:\n      AvailabilityZone:\n        Fn::Select:\n          - 1\n          - Fn::GetAZs: ''\n      VpcId: !Ref VPC\n      CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']\n      MapPublicIpOnLaunch: true\n\n  InternetGateway:\n    Condition: CreateVPC\n    Type: AWS::EC2::InternetGateway\n\n  GatewayAttachment:\n    Condition: CreateVPC\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n\n  PublicRouteTable:\n    Condition: CreateVPC\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n\n  PublicRoute:\n    Condition: CreateVPC\n    DependsOn: GatewayAttachment\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      DestinationCidrBlock: '0.0.0.0/0'\n      GatewayId: !Ref InternetGateway\n\n  PublicSubnetOneRouteTableAssociation:\n    Condition: CreateVPC\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetOne\n      RouteTableId: !Ref PublicRouteTable\n\n  PublicSubnetTwoRouteTableAssociation:\n    Condition: CreateVPC\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetTwo\n      RouteTableId: !Ref PublicRouteTable\n\n  # ECS cluster to run stuff\n  ECSCluster:\n    Type: AWS::ECS::Cluster\n    Properties:\n      CapacityProviders:\n        - FARGATE\n      DefaultCapacityProviderStrategy:\n        - CapacityProvider: FARGATE\n          Weight: 1\n\n  # SGs for Fargate and API Gateway\n  FargateContainerSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: Access to the Fargate containers\n      VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n\n  ApiGatewaySecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: API Gateway traffic for 1Password Connect\n      VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n\n  # Security group rules\n  ApiGatewayEgress: # lets API Gateway talk to Fargate\n    Type: AWS::EC2::SecurityGroupEgress\n    Properties:\n      GroupId: !Ref ApiGatewaySecurityGroup\n      IpProtocol: tcp\n      FromPort: 8080\n      ToPort: 8080\n      DestinationSecurityGroupId: !Ref FargateContainerSecurityGroup\n\n  ConnectIngress:\n    Type: AWS::EC2::SecurityGroupIngress\n    Properties:\n      GroupId: !Ref FargateContainerSecurityGroup\n      IpProtocol: tcp\n      FromPort: 8080\n      ToPort: 8080\n      SourceSecurityGroupId: !Ref ApiGatewaySecurityGroup\n\n  ConnectSelfIngress:\n    Type: AWS::EC2::SecurityGroupIngress\n    Properties:\n      Description: Ingress from other containers in the same security group\n      GroupId: !Ref FargateContainerSecurityGroup\n      IpProtocol: -1\n      SourceSecurityGroupId: !Ref FargateContainerSecurityGroup\n\n  ConnectPublicEgress:\n    Type: AWS::EC2::SecurityGroupEgress\n    Properties:\n      GroupId: !Ref FargateContainerSecurityGroup\n      IpProtocol: tcp\n      FromPort: 443\n      ToPort: 443\n      CidrIp: 0.0.0.0/0\n      Description: HTTPS to external services (e.g., Docker Hub)\n\n  # IAM roles for ECS\n  ECSTaskExecutionRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: '2012-10-17'\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: ecs-tasks.amazonaws.com\n            Action: sts:AssumeRole\n      Policies:\n        - PolicyName: AmazonECSTaskExecutionRolePolicy\n          PolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n              - Effect: Allow\n                Action:\n                  - ecr:GetAuthorizationToken\n                  - ecr:BatchCheckLayerAvailability\n                  - ecr:GetDownloadUrlForLayer\n                  - ecr:BatchGetImage\n                  - logs:CreateLogStream\n                  - logs:PutLogEvents\n                Resource: '*'\n        - PolicyName: SecretAccess\n          PolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n              - Effect: Allow\n                Action:\n                  - secretsmanager:GetSecretValue\n                Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:CredentialsSecret-${AWS::StackName}*'\n\n  ECSRole:\n    Type: AWS::IAM::Role\n    Properties:\n      AssumeRolePolicyDocument:\n        Version: '2012-10-17'\n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: ecs-tasks.amazonaws.com\n            Action: sts:AssumeRole\n      Policies:\n        - PolicyName: ECSPermissions\n          PolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n              - Effect: Allow\n                Action:\n                  - ec2:AttachNetworkInterface\n                  - ec2:CreateNetworkInterface\n                  - ec2:DeleteNetworkInterface\n                  - ec2:DescribeNetworkInterfaces\n                  - ec2:DetachNetworkInterface\n                Resource: '*'\n\n  # Logs in CloudWatch\n  CloudWatchLogsGroup:\n    Type: AWS::Logs::LogGroup\n    Properties:\n      LogGroupName: onepassword-connect\n      RetentionInDays: 30\n\n  # Service discovery for internal routing\n  ServiceDiscoveryNamespace:\n    Type: AWS::ServiceDiscovery::PrivateDnsNamespace\n    Properties:\n      Description: Private DNS namespace for 1Password Connect\n      Vpc: !If [CreateVPC, !Ref VPC, !Ref VPCID]\n      Name: onepassword\n\n  ConnectServiceDiscovery:\n    Type: AWS::ServiceDiscovery::Service\n    DependsOn:\n      - ServiceDiscoveryNamespace\n    Properties:\n      DnsConfig:\n        DnsRecords:\n          - TTL: 60\n            Type: SRV\n      Name: connect\n      NamespaceId: !Ref ServiceDiscoveryNamespace\n      HealthCheckCustomConfig:\n        FailureThreshold: 1\n\n  # Task def for the Connect containers\n  TaskDefinition:\n    Type: AWS::ECS::TaskDefinition\n    DependsOn:\n      - ECSTaskExecutionRole\n      - CredentialsSecret\n      - CloudWatchLogsGroup\n    Properties:\n      Cpu: 256\n      Memory: 512\n      NetworkMode: awsvpc\n      RequiresCompatibilities:\n        - FARGATE\n      ExecutionRoleArn: !Ref ECSTaskExecutionRole\n      TaskRoleArn: !Ref ECSRole\n      Volumes:\n        - Name: data\n      ContainerDefinitions:\n        - Name: connect-api\n          Image: 1password/connect-api:latest\n          PortMappings:\n            - ContainerPort: 8080\n              Protocol: tcp\n          MountPoints:\n            - ContainerPath: /home/opuser/.op/data\n              SourceVolume: data\n          Secrets:\n            - Name: OP_SESSION\n              ValueFrom: !Ref CredentialsSecret\n          LogConfiguration:\n            LogDriver: awslogs\n            Options:\n              awslogs-group: !Ref CloudWatchLogsGroup\n              awslogs-region: !Ref AWS::Region\n              awslogs-stream-prefix: connect-api\n        - Name: connect-sync\n          Image: 1password/connect-sync:latest\n          MountPoints:\n            - ContainerPath: /home/opuser/.op/data\n              SourceVolume: data\n          Environment:\n            - Name: OP_HTTP_PORT\n              Value: '8081'\n          Secrets:\n            - Name: OP_SESSION\n              ValueFrom: !Ref CredentialsSecret\n          LogConfiguration:\n            LogDriver: awslogs\n            Options:\n              awslogs-group: !Ref CloudWatchLogsGroup\n              awslogs-region: !Ref AWS::Region\n              awslogs-stream-prefix: connect-sync\n\n  # ECS service to run the containers\n  ConnectService:\n    Type: AWS::ECS::Service\n    DependsOn:\n      - TaskDefinition\n      - ApiGatewayStage\n      - ConnectServiceDiscovery\n      - FargateContainerSecurityGroup\n    Properties:\n      ServiceName: connect\n      Cluster: !Ref ECSCluster\n      LaunchType: FARGATE\n      DesiredCount: 1\n      TaskDefinition: !Ref TaskDefinition\n      NetworkConfiguration:\n        AwsvpcConfiguration:\n          AssignPublicIp: ENABLED\n          SecurityGroups:\n            - !Ref FargateContainerSecurityGroup\n          Subnets: !If\n            - CreateVPC\n            - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]\n            - !Ref PublicSubnets\n      ServiceRegistries:\n        - ContainerName: connect-api\n          ContainerPort: 8080\n          RegistryArn: !GetAtt ConnectServiceDiscovery.Arn\n\n  # API Gateway setup\n  VpcLink:\n    Type: AWS::ApiGatewayV2::VpcLink\n    Properties:\n      Name: connect-vpc-link\n      SecurityGroupIds:\n        - !Ref ApiGatewaySecurityGroup\n      SubnetIds: !If\n        - CreateVPC\n        - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]\n        - !Ref PublicSubnets\n\n  ApiGateway:\n    Type: AWS::ApiGatewayV2::Api\n    Properties:\n      Name: onepassword-connect\n      ProtocolType: HTTP\n      Description: API Gateway for 1Password Connect\n\n  ApiGatewayIntegration:\n    Type: AWS::ApiGatewayV2::Integration\n    DependsOn:\n      - ApiGateway\n      - VpcLink\n    Properties:\n      ApiId: !Ref ApiGateway\n      IntegrationType: HTTP_PROXY\n      IntegrationMethod: ANY\n      ConnectionType: VPC_LINK\n      ConnectionId: !Ref VpcLink\n      IntegrationUri: !GetAtt ConnectServiceDiscovery.Arn\n      PayloadFormatVersion: 1.0\n\n  ApiGatewayRoute:\n    Type: AWS::ApiGatewayV2::Route\n    DependsOn:\n      - ApiGateway\n      - ApiGatewayIntegration\n    Properties:\n      ApiId: !Ref ApiGateway\n      RouteKey: $default\n      AuthorizationType: NONE\n      Target: !Join\n        - /\n        - - integrations\n          - !Ref ApiGatewayIntegration\n\n  ApiGatewayStage:\n    Type: AWS::ApiGatewayV2::Stage\n    DependsOn:\n      - ApiGateway\n      - ApiGatewayRoute\n    Properties:\n      StageName: $default\n      ApiId: !Ref ApiGateway\n      AutoDeploy: true\n\nOutputs:\n  ExternalUrl:\n    Description: The URL of the API Gateway for 1Password Connect\n    Value: !GetAtt ApiGateway.ApiEndpoint\n    Export:\n      Name: !Join [ ':', [ !Ref AWS::StackName, 'ExternalUrl' ] ]\n      "
  },
  {
    "path": "examples/beta/azure-container-apps-arm/README.md",
    "content": "# Deploy 1Password Connect Server on Azure Container Apps using the Azure Portal\n\n_Learn how to deploy 1Password Connect Server on the [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/#overview) service._\n\nThis deployment consists of two [containers](https://learn.microsoft.com/en-us/azure/container-apps/containers): One for the Connect API and another for Connect Sync. There's also an [ingress](https://learn.microsoft.com/en-us/azure/container-apps/ingress-overview) for the API container. There are a few benefits to deploying 1Password Connect Server on Azure Container Apps:\n\n- **Low cost:** For standard deployments, the service will host your Connect Server for ~$16 USD/month (as of January 2024). Container Apps pricing is variable based on activity, and you can learn more on [Microsoft's pricing page](https://azure.microsoft.com/en-us/pricing/details/container-apps/).\n- **Automatic DNS record management:** You don't need to manage a DNS record. Azure Container Apps automatically provides a unique one for your Connect Server domain.\n- **Automatic TLS certificate management:** Azure Container Apps automatically handles TLS certificate management on your behalf.\n\n## Before you begin\n\nBefore you begin, ensure you have an Azure account with permission to create a Container App. You'll also need the `1password-credentials.json` file for your 1Password Connect Server, which contains the credentials for authenticating the server.\n\n> [!NOTE]\n> If you don't have an Azure account, you can sign up for a free trial with starting credit: https://azure.microsoft.com/free/\n\n## Step 1: Download the template file\n\n1. Download the [ARM(Azure Resource Manager) template](./aca-op-connect-server-template.json) file using the download icon at the top right.\n\n## Step 2: Create the Container App\n\n1. Sign in to the Azure Portal and go to the [Deploy a custom template](https://portal.azure.com/#create/Microsoft.Template) page.\n2. Click **Build your own template in the editor**.\n3. Click **Load file** and upload the template file you downloaded earlier. Then click **Save**.\n4. Fill out the following fields:\n   - **Subscription** : Choose the subscription you prefer.\n   - **Resource group**: Choose an existing Resource Group or create a new one using **Create new** button.\n   - **Region**: Choose the region you prefer.\n   - **Container App Name**: Enter a name you'd like to use, default will be `op-connect-con-app`.\n   - **Container App Env Name**: Enter a name you'd like to use, default will be `op-connect-con-env`.\n   - **Container App Log Analytics Name**: Enter a name you'd like to use, default will be `op-connect-con-app-log-analytics`.\n   - **Credentials** : Paste the entire contents of the `1password-credentials.json` file.\n5. Click **Review + create**.\n6. Once the validation succeeds, click **Create**. It is expected to take a couple of minutes to complete the deployment.\n\n## Step 3: Test your Connect Server\n\nOnce your deployment is complete, click **Go to resource group** and click on the container app you created.\n\nTo test if your Connect Server is online, choose **Overview** in your application's sidebar, then copy your **Application Url** link. This is your **Connect Server URL**. Use your 1Password Connect access token to interact with the API (e.g., via a `curl` request or a client application) to verify that your Connect Server is connected to your 1Password account.\n\n```sh\ncurl --silent --show-error --request GET --header \"Accept: application/json\" \\\n  --header \"Authorization: Bearer mF_9.B5f-4.1JqM\" \\\n  https://op-connect.example.com/v1/vaults\n```\n\n<details>\n<summary>Example JSON response:</summary>\n\n```json\n[\n  {\n    \"attributeVersion\": 1,\n    \"contentVersion\": 9,\n    \"createdAt\": \"2025-03-21T13:06:35Z\",\n    \"id\": \"hgm3sn37vkj3lsdkeouq46wyca\",\n    \"items\": 5,\n    \"name\": \"REDACTED\",\n    \"type\": \"USER_CREATED\",\n    \"updatedAt\": \"2025-04-18T16:38:40Z\"\n  },\n  {\n    \"attributeVersion\": 1,\n    \"contentVersion\": 2,\n    \"createdAt\": \"2025-03-21T02:15:33Z\",\n    \"id\": \"mn4w65hq6ar7nc2fc7qgoru3ki\",\n    \"items\": 1,\n    \"name\": \"REDACTED\",\n    \"type\": \"USER_CREATED\",\n    \"updatedAt\": \"2025-03-21T02:16:32Z\"\n  },\n  {\n    \"attributeVersion\": 1,\n    \"contentVersion\": 94,\n    \"createdAt\": \"2025-03-21T15:20:41Z\",\n    \"id\": \"z4s56cbejab6q6urod3l5icqky\",\n    \"items\": 3,\n    \"name\": \"REDACTED\",\n    \"type\": \"USER_CREATED\",\n    \"updatedAt\": \"2025-04-29T02:49:13Z\"\n  }\n]\n```\n\n</details>\n<br />\n\n## Step 4: Integrate with your application\n\nTo use the Connect Server, configure your application to communicate with the Connect Server URL using the access token provided in your 1Password account. Follow the [1Password Connect integration guide](https://developer.1password.com/docs/connect) to set up your application.\n\n<hr>\n\n## Update your Connect Server in the Azure Portal\n\n> [!TIP]\n> Check for 1Password Connect Server updates on the [1Password releases page](https://releases.1password.com/).\n\n1. Within your deployed 1Password Connect Server Container App in the [Azure Container Apps Portal](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.App%2FcontainerApps), select **Containers** from the sidebar.\n2. Click **Edit and deploy**.\n3. Select the checkbox next to your **connect-api** container, then choose **Edit**.\n4. Change the version number in the **Image and Tag** field, `1password/connect-api:latest`, to match the latest version from our [1Password releases page](https://releases.1password.com/).\n5. Repeat for the **connect-sync** container, updating `1password/connect-sync:latest` to the same version.\n6. Select **Save**.\n7. Select **Create** to deploy a new revision using the updated images.\n8. Test your Connect Server URL with your access token to verify the update.\n\n## Get help\n\n### How to update the **credentials** secret\n\nTo use a new `1password-credentials.json` credentials file for your Connect Server, replace the secret in your Container App:\n\n<details>\n<summary>Replace your <code>credentials</code> secret using the Azure Portal</summary>\n\n1. Open the Azure Portal and go to the [Container Apps](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.App%2FcontainerApps) page.\n2. Choose **Secrets** from the Settings section in the sidebar.\n3. Edit the **credentials** secret and paste the entire contents of your new `1password-credentials.json` file.\n4. Select the checkbox and click **Save**.\n5. Choose the **Revisions** from the Application section in the sidebar.\n6. Click your current active revision and choose **Restart** in the details pane.\n7. Test your Connect Server URL with your new access token to [test your Connect Server](#step-3-test-your-connect-server).\n</details>"
  },
  {
    "path": "examples/beta/azure-container-apps-arm/aca-op-connect-server-template.json",
    "content": "{\n  \"$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {\n    \"logAnalyticsWorkspaceName\": {\n      \"type\": \"string\",\n      \"defaultValue\": \"op-connect-con-app-log-analytics\",\n      \"metadata\": {\n        \"description\": \"The name of the Log Analytics Workspace.\"\n      }\n    },\n    \"containerAppName\": {\n      \"type\": \"string\",\n      \"defaultValue\": \"op-connect-con-app\",\n      \"metadata\": {\n        \"description\": \"The name of the Container App.\"\n      }\n    },\n    \"containerAppEnvName\": {\n      \"type\": \"string\",\n      \"defaultValue\": \"op-connect-con-env\",\n      \"metadata\": {\n        \"description\": \"The name of the Container App Environment.\"\n      }\n    },\n    \"credentials\": {\n      \"type\": \"SecureString\",\n      \"metadata\": {\n        \"description\": \"The plain text contents of 1password-credentials.json.\"\n      }\n    }\n  },\n  \"resources\": [\n    {\n      \"type\": \"Microsoft.OperationalInsights/workspaces\",\n      \"apiVersion\": \"2022-10-01\",\n      \"name\": \"[parameters('logAnalyticsWorkspaceName')]\",\n      \"location\": \"[resourceGroup().location]\",\n      \"properties\": {\n        \"sku\": {\n          \"name\": \"PerGB2018\"\n        }\n      }\n    },\n    {\n      \"type\": \"Microsoft.App/managedEnvironments\",\n      \"apiVersion\": \"2023-05-01\",\n      \"name\": \"[parameters('containerAppEnvName')]\",\n      \"location\": \"[resourceGroup().location]\",\n      \"dependsOn\": [\n        \"[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]\"\n      ],\n      \"properties\": {\n        \"appLogsConfiguration\": {\n          \"destination\": \"log-analytics\",\n          \"logAnalyticsConfiguration\": {\n            \"customerId\": \"[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))).customerId]\",\n            \"sharedKey\": \"[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]\"\n          }\n        },\n        \"zoneRedundant\": false\n      }\n    },\n    {\n      \"type\": \"Microsoft.App/containerApps\",\n      \"apiVersion\": \"2023-05-01\",\n      \"name\": \"[parameters('containerAppName')]\",\n      \"location\": \"[resourceGroup().location]\",\n      \"dependsOn\": [\n        \"[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]\"\n      ],\n      \"properties\": {\n        \"managedEnvironmentId\": \"[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]\",\n        \"configuration\": {\n          \"secrets\": [\n            {\n              \"name\": \"credentials\",\n              \"value\": \"[parameters('credentials')]\"\n            }\n          ],\n          \"activeRevisionsMode\": \"Single\",\n          \"ingress\": {\n            \"external\": true,\n            \"targetPort\": 8080,\n            \"allowInsecure\": false,\n            \"traffic\": [\n              {\n                \"latestRevision\": true,\n                \"weight\": 100\n              }\n            ]\n          }\n        },\n        \"template\": {\n          \"containers\": [\n            {\n              \"name\": \"connect-api\",\n              \"image\": \"docker.io/1password/connect-api:latest\",\n              \"resources\": {\n                \"cpu\": 0.25,\n                \"memory\": \"0.5Gi\"\n              },\n              \"volumeMounts\": [\n                {\n                  \"volumeName\": \"data\",\n                  \"mountPath\": \"/home/opuser/.op/data\"\n                },\n                {\n                  \"volumeName\": \"credentials\",\n                  \"mountPath\": \"/home/opuser/.op/1password-credentials.json\",\n                  \"subPath\": \"1password-credentials.json\"\n                }\n              ]\n            },\n            {\n              \"name\": \"connect-sync\",\n              \"image\": \"docker.io/1password/connect-sync:latest\",\n              \"resources\": {\n                \"cpu\": 0.25,\n                \"memory\": \"0.5Gi\"\n              },\n              \"env\": [\n                {\n                  \"name\": \"OP_HTTP_PORT\",\n                  \"value\": \"8081\"\n                }\n              ],\n              \"volumeMounts\": [\n                {\n                  \"volumeName\": \"data\",\n                  \"mountPath\": \"/home/opuser/.op/data\"\n                },\n                {\n                  \"volumeName\": \"credentials\",\n                  \"mountPath\": \"/home/opuser/.op/1password-credentials.json\",\n                  \"subPath\": \"1password-credentials.json\"\n                }\n              ]\n            }\n          ],\n          \"volumes\": [\n            {\n              \"name\": \"data\",\n              \"storageType\": \"EmptyDir\"\n            },\n            {\n              \"name\": \"credentials\",\n              \"storageType\": \"Secret\",\n              \"secrets\": [\n                {\n                  \"secretRef\": \"credentials\",\n                  \"path\": \"1password-credentials.json\"\n                }\n              ]\n            }\n          ],\n          \"scale\": {\n            \"minReplicas\": 1,\n            \"maxReplicas\": 1\n          }\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "examples/beta/docker/README.md",
    "content": "# [Beta] Deploy 1Password Connect Server using Docker Swarm\n\nThis example describes how to deploy 1Password Connect Server as a [stack](https://docs.docker.com/engine/swarm/stack-deploy/) using [Docker Swarm](https://docs.docker.com/engine/swarm/key-concepts/#what-is-a-swarm). The stack includes two [services](https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/) (one each for the Connect API and SYNC containers), a [Docker secret](https://docs.docker.com/engine/swarm/secrets/) for the `1password-credentials.json` file, and optional secrets and configuration required only for using a custom TLS certificate.\n\n## In this folder\n\n- [`README.md`](./README.md): the document that you are reading. 👋😃\n- [`compose.template.yaml`](./compose.template.yaml): a [Compose Specification](https://docs.docker.com/compose/compose-file/) format [Compose file](https://docs.docker.com/compose/compose-file/03-compose-file/) for 1Password Connect Server\n- [`compose.lets-encrypt.yaml`](./compose.lets-encrypt.yaml): an [override configuration](https://docs.docker.com/compose/multiple-compose-files/merge/) to merge the configuration necessary for Let's Encrypt\n- [`compose.http.yaml`](./compose.http.yaml): optional configuration for exposing an HTTP port on the Docker host for forwarding plain-text traffic within a private network from a public TLS termination endpoint\n- [`compose.custom-tls.yaml`](./compose.custom-tls.yaml): optional configuration for enabling a self-managed TLS certificate\n- [`connect-api.env`](./connect-api.env): an environment file used to customize the connect-api service\n- [`connect-sync.env`](./connect-sync.env): an environment file used to customize the connect-sync service\n\n## Overview\n\nThe open source Docker Engine tooling can be used to deploy 1Password Connect Server on any supported Linux distribution. A single-node swarm is sufficient for most deployments.\n\n## Prerequisites\n\n- AMD64/ARM64 VM or bare metal server with a Docker-supported Linux distribution (e.g. Ubuntu, Debian, Fedora, etc.)\n- Docker Engine (see [Docker Engine installation overview](https://docs.docker.com/engine/install/#server)) installed on the Linux server\n- a public DNS A record pointing to the Linux server\n- SSH access to the Linux server\n- [Docker Desktop](https://docs.docker.com/engine/install/#desktop) or Docker Engine installed on a machine with access to the Linux server and credentials from your 1Password account\n\n## Get started\n\n> [!Note]\n> Before proceeding, review the [Get Started](https://developer.1password.com/docs/connect/get-started/) page from our 1Password Developer page.\n\nCreate a public DNS record that points to the public IP address of the Linux server for your Connect server if you wish to have this publicly available. For example, `op-connect-server.example.com`.\n\n### 🛠️ Prepare the Linux server\n\nOn the Linux machine that you will be using as the Docker host for your Connect server:\n\n1. If you haven't already done so, [install Docker Engine](https://docs.docker.com/engine/install/#server) on the Linux server. Follow the Server instructions; Docker Desktop is not needed for the Linux server.\n2. Follow the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) as noted in the documentation to enable running Docker as a non-root user and ensure that Docker Engine starts when the Linux server boots.\n\n### 👨‍💻 Prepare your desktop\n\nAll following steps should be run on the same computer where you are already using 1Password, or another machine that can access the Linux server using SSH and has access to the `1password-credentials.json` file from the integration setup:\n\n1. If you haven't already done so, install Docker. You can use [Docker Desktop](https://docs.docker.com/engine/install/#desktop), [install Docker Engine from binaries](https://docs.docker.com/engine/install/binaries/), or install Docker using your favorite package manager.\n\n2. Open your preferred terminal. Clone this repository and switch to this directory:\n\n   ```sh\n   git clone https://github.com/1Password/connect.git\n   cd ./connect/examples/beta/docker\n   ```\n\n3. Save the `1password-credentials.json` file from [the Connect Server setup](https://start.1password.com/developer-tools/infrastructure-secrets/connect/) to this working directory.\n\n4. Open `connect-api.env` in your favorite text editor. Set the value of `OP_TLS_DOMAIN` to the fully qualified domain name of the public DNS record for your Connect server if you wish to have this publicly available, created in [Get started](#get-started). For example:\n\n   ```dotenv\n   # ...\n   OP_TLS_DOMAIN=op-connect-server.example.com\n\n   # ...\n   ```\n\n   Save the file.\n\n5. Create a Docker context to use for connecting to the Linux server:\n\n   _Example command:_\n\n   ```sh\n   docker context create op-connect \\\n       --description \"1Password Connect Server Docker host\" \\\n       --docker host=ssh://user@op-connect-server.example.com\n   ```\n\n   Copy the example command to a text editor. Replace `user` with the appropriate username for your Linux server and `op-connect-server.example.com` with the host name or IP address used for SSH access to the Linux server before running the command in your terminal.\n\n   > **Note**\n   >\n   > 🔑 You can store your SSH keys in 1Password and authenticate this and other SSH workflows without writing a\n   > private key to disk using the [1Password SSH agent](https://developer.1password.com/docs/ssh/get-started).\n\n6. Switch to the Docker context you just created to connect to the Docker host:\n\n   ```sh\n   docker context use op-connect\n   ```\n\n### 🪄 Create a swarm\n\nRun this command to create the swarm:\n\n```sh\ndocker swarm init # --advertise-addr 192.0.2.1\n```\n\n> **Note**\n>\n> Additional nodes may be optionally added to a swarm for fault tolerance. This command adds the Linux server as the\n> first (and only) node in the swarm, but Docker _requires_ a unique IP address to advertise to other nodes (even if\n> no other nodes will be added). See [How nodes work](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/)\n> in the Docker documentation for more details.\n>\n> If multiple IP addresses are detected, Docker returns an error; uncomment the `--advertise-addr` parameter (delete\n> `#`), replace the example IP (`192.0.2.1`) with the appropriate IP address, and run the command again.\n\n## Deploy 1Password Connect server\n\nUse the Compose template to output a canonical configuration for use with Docker Swarm and create the stack from this configuration inline. Your Connect server should automatically acquire and manage a TLS certificate from Let's Encrypt on your behalf if configured this way:\n\n```sh\ndocker stack config --compose-file ./compose.template.yaml \\ --compose-file ./compose.lets-encrypt.yaml |\n  docker stack deploy --compose-file - op-connect\n```\n\n## Test your Connect server\n\nRun this command to view logs from the service for the Connect server container:\n\n```sh\ndocker service logs op-connect_api --raw\n```\n\nYour **Connect server URL** is based on the fully qualified domain name of the DNS record created in [Get started](#get-started). For example: `https://op-connect.example.com`. Replace `mF_9.B5f-4.1JqM` with your bearer token and `https://op-connect.example.com` with your Connect server URL to test the connection and view status information.\n\n```sh\ncurl --silent --show-error --request GET --header \"Accept: application/json\" \\\n  --header \"Authorization: Bearer mF_9.B5f-4.1JqM\" \\\n  https:/op-connect.example.com/health/v1/vaults\n```\n\n<details>\n<summary>Example JSON response:</summary>\n\n```json\n[\n  {\n    \"attributeVersion\": 1,\n    \"contentVersion\": 9,\n    \"createdAt\": \"2025-03-21T13:06:35Z\",\n    \"id\": \"hgm3sn37vkj3lsdkeouq46wyca\",\n    \"items\": 5,\n    \"name\": \"REDACTED\",\n    \"type\": \"USER_CREATED\",\n    \"updatedAt\": \"2025-04-18T16:38:40Z\"\n  },\n  {\n    \"attributeVersion\": 1,\n    \"contentVersion\": 2,\n    \"createdAt\": \"2025-03-21T02:15:33Z\",\n    \"id\": \"mn4w65hq6ar7nc2fc7qgoru3ki\",\n    \"items\": 1,\n    \"name\": \"REDACTED\",\n    \"type\": \"USER_CREATED\",\n    \"updatedAt\": \"2025-03-21T02:16:32Z\"\n  },\n  {\n    \"attributeVersion\": 1,\n    \"contentVersion\": 94,\n    \"createdAt\": \"2025-03-21T15:20:41Z\",\n    \"id\": \"z4s56cbejab6q6urod3l5icqky\",\n    \"items\": 3,\n    \"name\": \"REDACTED\",\n    \"type\": \"USER_CREATED\",\n    \"updatedAt\": \"2025-04-29T02:49:13Z\"\n  }\n]\n```\n\n</details>\n<br />\n\n## Update 1Password Connect server\n\nSwarm mode in Docker Engine uses a declarative service model. Services will automatically restart tasks when updating their configuration.\n\nUse the Docker context from [Prepare your desktop](#-prepare-your-desktop) to connect to your Docker host and manage your stack.\n\nUpdate the `op-connect_api` and `op-connect_sync` services with the new image tags from the `1password/connect-api` and `1password/connect-sync`repository on Docker Hub to update your Connect server to a new version:\n\n- https://hub.docker.com/r/1password/connect-api/tags\n- https://hub.docker.com/r/1password/connect-sync/tags\n\n```sh\ndocker service update op-connect_api --image 1password/connect-api:latest && \\\n   docker service update op-connect_sync --image 1password/connect-sync:latest\n\n```\n\n## Rotate credentials\n\nDocker secrets are immutable and cannot be removed while in use by a Swarm service. To use new secret values in your stack, you must remove the existing secret from the service configuration, replace the Docker secret (or add a new one), and update the service configuration to mount the new secret value.\n\nFor example, if you create new credentials for the Connect server:\n\n1. Scale down the `op-connect_api` and `op-connect_sync services to shut down the running Connect server.\n\n   ```sh\n   docker service scale op-connect_api=0 op-connect_sync=0\n   ```\n\n2. Update the service definition to unmount the the `1password-credentials.json` Docker secret:\n\n   ```sh\n   docker service update --secret-rm credentials op-connect_api && \\\n      docker service update --secret-rm credentials op-connect_sync\n   ```\n\n3. Remove the secret from the swarm:\n\n   ```sh\n   docker secret rm credentials\n   ```\n\n4. Copy the new `1password-credentials.json` file from your 1Password account to your working directory. Create a new `credentials` Docker secret using the new file:\n\n   ```sh\n   docker secret create credentials ./1password-credentials.json\n   ```\n\n5. Update the service to mount the new `1password-credentials.json` secret:\n\n   ```sh\n   docker service update --secret-add source=credentials,target=/home/opuser/.op/1password-credentials.json,uid=\"999\",gid=\"999\",mode=0440 op-connect_api && \\\n      docker service update --secret-add source=credentials,target=/home/opuser/.op/1password-credentials.json,uid=\"999\",gid=\"999\",mode=0440 op-connect_sync\n\n   ```\n\n6. Scale the `op-connect_api` and `op-connect_sync` services back up to reboot your Connect server:\n\n   ```sh\n   docker service scale op-connect_api=1 op-connect_sync=1\n   ```\n\n[Test your Connect server](#-test-your-Connect server) using the new bearer token associated with the new `1password-credentials.json` file.\n\nA similar process can be used to update the values for any other Docker secrets used in your configuration.\n\n## Appendix: Customize your Connect server\n\nMany Connect server configuration changes can be made by adding or removing environment variables. These can be customized by making changes to [`connect-api.env`](./connect-api.env) and [`connect-sync.env`](./connect-sync.env) (that can be committed to your source control) before deploying (or redeploying) your Connect server using the `docker stack deploy` command. For some use cases, it may be desirable to update the configuration \"on the fly\" from your terminal.\n\nFor example, to reboot your Connect server with debug logging enabled:\n\n```sh\ndocker service update op-connect_api \\\n    --env-add OP_DEBUG=1\n```\n\nTo turn off debug logging and inject some color into the logs in your console:\n\n```sh\ndocker service update op-connect_api \\\n    --env-rm OP_DEBUG \\\n    --env-add OP_PRETTY_LOGS=1\n```\n\n_Pretty logs pair nicely with the `--raw` parameter of the `docker service logs` command). 🤩_\n\n### 🔒 Advanced TLS options\n\nIdentity providers strictly require an HTTPS endpoint with a valid TLS certificate to use for the Connect server URL. Your 1Password Connect server includes an optional CertificateManager component that (by default) acquires and manages a TLS certificate using Let's Encrypt, and terminates TLS traffic at the Connect server container using this certificate. This requires port 443 of the Docker host to be publicly accessible to ensure Let's Encrypt can initiate an inbound connection to your Connect server.\n\nOther supported options include:\n\n#### External load balancer or reverse proxy\n\nTo terminate TLS traffic at another public endpoint and redirect private traffic to a Docker host in your private network, a Connect server can be configured to disable the CertificateManager component and serve plain-text HTTP traffic on port 80 of the Docker host. CertificateManager will be enabled if a value is set for the `OP_TLS_DOMAIN` variable, so any value set in `connect-api.env` must be removed (or this line must be commented out). The included `compose.http.yaml` file can be used to set up the port mapping when deploying (or redeploying) a Connect server:\n\n```sh\ndocker stack config \\\n    --compose-file ./compose.template.yaml \\\n    --compose-file ./compose.http.yaml |\n        docker stack deploy --compose-file - op-connect\n```\n\n#### Self-managed TLS certificate\n\nYou may supply your own TLS certificate with CertificateManager instead of invoking Let's Encrypt. The value set for `OP_TLS_DOMAIN` must match the common name of the certificate.\n\nSave the public and private certificate key files as `certificate.pem` and `key.pem` (respectively) to the working directory and use the included `compose.custom-tls.yaml` file when deploying your Connect server to create Docker secrets and configure a Connect server use this certificate when terminating TLS traffic:\n\n```sh\ndocker stack config \\\n    --compose-file ./compose.template.yaml \\\n    --compose-file ./compose.custom-tls.yaml |\n        docker stack deploy --compose-file - op-connect\n```\n"
  },
  {
    "path": "examples/beta/docker/compose.custom-tls.yaml",
    "content": "services:\n  api:\n    environment: [OP_TLS_CERT_FILE=/home/opuser/.op/tls.crt, OP_TLS_KEY_FILE=/home/opuser/.op/tls.key]\n    ports: [443:8443]\n    secrets:\n      - source: op-tls-crt\n        target: /home/opuser/.op/tls.crt\n        uid: \"999\"\n        gid: \"999\"\n        mode: 0440\n      - source: op-tls-key\n        target: /home/opuser/.op/tls.key\n        uid: \"999\"\n        gid: \"999\"\n        mode: 0440\nsecrets:\n  op-tls-crt:\n    file: certificate.pem\n    name: op-tls-crt\n  op-tls-key:\n    file: key.pem\n    name: op-tls-key\n"
  },
  {
    "path": "examples/beta/docker/compose.http.yaml",
    "content": "services:\n  api:\n    ports: [80:8080]\n"
  },
  {
    "path": "examples/beta/docker/compose.lets-encrypt.yaml",
    "content": "services:\n  api:\n    environment:\n      OP_TLS_USE_LETSENCRYPT: 1\n    ports: [443:8443]\n"
  },
  {
    "path": "examples/beta/docker/compose.template.yaml",
    "content": "services:\n  api:\n    image: 1password/connect-api\n    networks: [op-connect]\n    deploy:\n      replicas: 1\n      restart_policy:\n        condition: any\n    secrets:\n      - source: credentials\n        target: /home/opuser/.op/1password-credentials.json\n        uid: \"999\"\n        gid: \"999\"\n        mode: 0400\n    volumes:\n      - data:/home/opuser/.op/data\n    env_file: connect-api.env\n    user: \"999:999\"\n\n  sync:\n    image: 1password/connect-sync\n    networks: [op-connect]\n    deploy:\n      replicas: 1\n      restart_policy:\n        condition: any\n    secrets:\n      - source: credentials\n        target: /home/opuser/.op/1password-credentials.json\n        uid: \"999\"\n        gid: \"999\"\n        mode: 0400\n    volumes:\n      - data:/home/opuser/.op/data\n    env_file: connect-sync.env\n    user: \"999:999\"\n\nvolumes:\n  data:\n\nsecrets:\n  credentials:\n    file: 1password-credentials.json\n    name: credentials\n\nnetworks:\n  op-connect:\n"
  },
  {
    "path": "examples/beta/docker/connect-api.env",
    "content": "# # To enable TLS termination using Let's Encrypt, set to the domain name of a public DNS record that points to the publicly available endpoint for your Connect server.\n# OP_TLS_DOMAIN=connect.example.com\n\n# # The logging level of the container.\n# # Acceptable values: info, error, debug\n# # Default value: info\n# OP_LOG_LEVEL=debug\n\n# The time (in seconds) to wait for the initial sync to complete.\n# Acceptable values: A time duration (for example, 1h, 30m, 20s).\n# Default value: 10s (10 seconds)\n# OP_SYNC_TIMEOUT=1m\n\n# # ---Manual bus configuration---\n\n# # The port for listening to incoming bus connections from other containers.\n# # Acceptable values: Any available port number.\n# # Default value: A random free port\n# OP_BUS_PORT=11223\n\n# # A comma-separated list of [hostname]:[bus port] pairs of other containers to connect to.\n# # Acceptable values: A comma-separated list of [hostname]:[bus port] pairs.\n# # Default value: No default because this variable is optional.\n# OP_BUS_PEERS=sync:11223\n"
  },
  {
    "path": "examples/beta/docker/connect-sync.env",
    "content": "# # The logging level of the container.\n# # Acceptable values: info, error, debug\n# # Default value: info\n# OP_LOG_LEVEL=debug\n\n# # ---Manual bus configuration---\n\n# # The port for listening to incoming bus connections from other containers.\n# # Acceptable values: Any available port number.\n# # Default value: A random free port\n# OP_BUS_PORT=11223\n\n# # A comma-separated list of [hostname]:[bus port] pairs of other containers to connect to.\n# # Acceptable values: A comma-separated list of [hostname]:[bus port] pairs.\n# # Default value: No default because this variable is optional.\n# OP_BUS_PEERS=api:11223\n"
  },
  {
    "path": "examples/docker/compose/README.md",
    "content": "# Docker Compose\n\nThe `docker-compose.yaml` file in this directory is a simple example of how to run 1Password Connect using Docker Compose.\n\n## Getting Started\n\nBefore you begin, make sure you have your Connect server's `1password-credentials.json` file on your machine. If the credentials file is not located in the same directory as the `docker-compose.yaml` file, update the `volumes` section for both the Sync and API containers:\n\n```yaml\nvolumes:\n  - \"<filepath_here>/1password-credentials.json:/home/opuser/.op/1password-credentials.json\"\n```\n\n### Start the Docker containers:\n```bash\ndocker-compose up [-d]\n```\n\nThe Connect API will be accessible through port `8080` on the host machine:\n```curl\ncurl \\\n-H \"Accept: application/json\" \\\n-H \"Authorization: Bearer $OP_API_TOKEN\" \\\nhttp://localhost:8080/v1/vaults\n```"
  },
  {
    "path": "examples/docker/compose/docker-compose.yaml",
    "content": "version: \"3.4\"\n\nservices:\n  op-connect-api:\n    image: 1password/connect-api:latest\n    ports:\n      - \"8080:8080\"\n    volumes:\n      - \"./1password-credentials.json:/home/opuser/.op/1password-credentials.json\"\n      - \"data:/home/opuser/.op/data\"\n  op-connect-sync:\n    image: 1password/connect-sync:latest\n    ports:\n      - \"8081:8080\"\n    volumes:\n      - \"./1password-credentials.json:/home/opuser/.op/1password-credentials.json\"\n      - \"data:/home/opuser/.op/data\"\n\nvolumes:\n  data:\n"
  },
  {
    "path": "examples/kubernetes/README.md",
    "content": "# Kubernetes Deployment\n\nThis is an example Kubernetes deployment for 1Password Connect that uses [cert-manager](https://cert-manager.io) to provision a TLS certificate for your external domain.\n\nIncluded in this folder are:\n\n- [`op-connect-deployment.yaml`](op-connect-deployment.yaml): Deployment resource to create the Pod for 1Password Connect\n  - `connect-sync` container to sync vault(s) from 1Password.com\n  - `connect-api` container to serve 1Password Connect API\n  - Shared ephemeral volume for data\n- [`op-connect-service.yaml`](op-connect-service.yaml): Service resource for connecting to the above Pod\n- [`op-connect-issuer.yaml`](op-connect-issuer.yaml): cert-manager ClusterIssuer resource to manage TLS certificates using [Let's Encrypt](https://letsencrypt.org/)\n- [`op-connect-ingress.yaml`](op-connect-ingress.yaml): Ingress resource to route TLS requests to 1Password Connect\n\n## Prerequisites\n\n- 1Password Connect server credentials file (see [Quick Start](/README.md#quick-start) in the root of this repository)\n- Kubernetes cluster with available [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), e.g.:\n  - [Ingress NGINX Controller](https://kubernetes.github.io/ingress-nginx/deploy/)\n  - [GKE Ingress](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress)\n  - [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/deploy/installation/)\n  - [Azure Application Gateway Ingress Controller](https://learn.microsoft.com/en-us/azure/application-gateway/tutorial-ingress-controller-add-on-new)\n- [cert-manager](https://cert-manager.io) installed on your cluster (see [Installation - cert-manager Documentation](https://cert-manager.io/docs/installation/))\n- A DNS provider in which you can create public records.\n\n## Getting started\n\n1. If you haven't already done so, save your `1password-credentials.json` file locally.\n\n2. Create a Kubernetes Secret for the credentials file:\n\n    ```sh\n    kubectl create secret generic op-credentials --from-file=1password-credentials.json=./1password-credentials.json\n    ```\n\n    > **Note**\n    >\n    > If saved with a different filename or somewhere other than the working\n    > directory, replace `./1password-credentials.json` with the path to your\n    > credentials file, i.e.:\n    >\n    > ```sh\n    > kubectl create secret generic op-credentials --from-file=1password-credentials.json=path/to/credentials-file.json\n    > ```\n\n3. Clone this repository and switch to this directory:\n\n    ```sh\n    git clone https://github.com/1Password/connect.git\n    cd ./connect/examples/kubernetes\n    ```\n\n4. Edit [`op-connect-issuer.yaml`](op-connect-issuer.yaml). Replace the value for `spec.acme.email` with a valid email address:\n\n    ```yaml\n    ...\n    spec:\n      acme:\n        ...\n        email: user@example.com\n    ...\n    ```\n\n    Save the file.\n\n5. Edit [`op-connect-ingress.yaml`](op-connect-ingress.yaml). Replace `connect.example.com` with the fully-qualified domain name you will use for 1Password Connect (use the same FQDN for both fields):\n\n    ```yaml\n    ...\n    spec: \n      rules:\n        - host: connect.example.com\n    ...\n    tls:\n      - hosts:\n        - connect.example.com\n    ...\n    ```\n\n    > **Warning**\n    >\n    > Depending on your chosen ingress controller, you may also need to add\n    > additional annotations or make other changes to this manifest.\n\n    Save the file.\n\n## Deploy 1Password Connect\n\n1. Apply the manifests to your cluster:\n\n    ```sh\n    kubectl apply -f .\n    ```\n\n    > **Note**\n    >\n    > The `.` in this command applies all `.yaml` and `.json` files in the\n    > working directory. If this includes the 1Password Connect credentials\n    > file, the output returned  by `kubectl` may include something similar to:\n    >\n    >>```output\n    >> error: error validating \"1password-credentials.json\":...\n    >> ```\n    >\n    > This error can safely be ignored.\n\n2. Get the IP address of the ingress controller:\n\n    ```sh\n    kubectl get ing/connect-ingress\n    ```\n\n    The IP address for this Ingress resource will be listed under the `ADDRESS` column.\n\n    > **Note**\n    >\n    > Depending on your cluster or cloud provider, it may be several minutes\n    > before an IP address is assigned. If there is no IP address listed yet,\n    > run the same command again in a few minutes.\n\n3. Create a DNS record that points to the above address. cert-manager will automatically acquire a TLS certificate on your behalf (this may take a few minutes).\n\n## Test the connection\n\nConfirm public access by connecting using HTTPS from outside your cluster:\n\n> **Note**\n>\n> You will need to set the value of the `$OP_API_TOKEN` variable to an active token for this Connect environment.\n\n```sh\ncurl \\\n  -X GET \\\n  -H \"Accept: application/json\" \\\n  -H \"Authorization: Bearer $OP_API_TOKEN\" \\\n  https://connect.example.com/v1/vaults\n```\n\nThis will also intialize the connection to 1Password.com and complete the first valut data sync.\n\n## Appendix\n\n### Updating 1Password Connect\n\nWhen a new version of 1Password Connect is released, set the image version for each of the containers to rollout a new revision:\n\n```sh\nOP_CONNECT_VERSION=1.5.7; kubectl set image deploy/op-connect \\\n  connect-api=1password/connect-api:$OP_CONNECT_VERSION \\\n  connect-sync=1password/connect-sync:$OP_CONNECT_VERSION\n```\n"
  },
  {
    "path": "examples/kubernetes/op-connect-deployment.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: op-connect\nspec:\n  selector:\n    matchLabels:\n      app: op-connect\n  template:\n    metadata:\n      labels:\n        app: op-connect\n    spec:\n      volumes:\n        - name: shared-data\n          emptyDir: {}\n        - name: credentials\n          secret:\n            secretName: op-credentials\n      initContainers:\n        - name: sqlite-permissions\n          image: alpine:3.12\n          command:\n            - \"/bin/sh\"\n            - \"-c\"\n          args:\n            - \"mkdir -p /home/opuser/.op/data && chown -R 999 /home/opuser && chmod -R 700 /home/opuser && chmod -f -R 600 /home/opuser/.op/config || :\"\n          volumeMounts:\n            - mountPath: /home/opuser/.op/data\n              name: shared-data\n      containers:\n        - name: connect-api\n          image: 1password/connect-api:latest\n          resources:\n            limits:\n              memory: \"128Mi\"\n              cpu: \"0.2\"\n          ports:\n            - name: connect-api\n              containerPort: 8080\n          env:\n            - name: OP_SESSION\n              value: /home/opuser/.config/1password-credentials.json\n          volumeMounts:\n            - mountPath: /home/opuser/.op/data\n              name: shared-data\n            - mountPath: /home/opuser/.config\n              name: credentials\n              readOnly: true\n          livenessProbe:\n            httpGet:\n              path: /heartbeat\n              port: connect-api\n            initialDelaySeconds: 3\n            periodSeconds: 3\n          readinessProbe:\n            httpGet:\n              path: /health\n              port: connect-api\n            initialDelaySeconds: 5\n            periodSeconds: 5\n        - name: connect-sync\n          image: 1password/connect-sync:latest\n          resources:\n            limits:\n              memory: \"128Mi\"\n              cpu: \"0.2\"\n          ports:\n            - name: connect-sync\n              containerPort: 8081\n          env:\n            - name: OP_SESSION\n              value: /home/opuser/.config/1password-credentials.json\n            - name: OP_HTTP_PORT\n              value: \"8081\"\n          volumeMounts:\n            - mountPath: /home/opuser/.op/data\n              name: shared-data\n            - mountPath: /home/opuser/.config\n              name: credentials\n              readOnly: true\n          livenessProbe:\n            httpGet:\n              path: /heartbeat\n              port: connect-sync\n            initialDelaySeconds: 3\n            periodSeconds: 3\n          readinessProbe:\n            httpGet:\n              path: /health\n              port: connect-sync\n            initialDelaySeconds: 5\n            periodSeconds: 5\n"
  },
  {
    "path": "examples/kubernetes/op-connect-ingress.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: connect-ingress\n  annotations:\n    # Use cert-manager ClusterIssuer\n    cert-manager.io/cluster-issuer: letsencrypt\n    # Allow cert-manager to edit this Ingress resource in-place\n    acme.cert-manager.io/http01-edit-in-place: \"true\"\n    # Issue temporary self-signed cert\n    cert-manager.io/issue-temporary-certificate: \"true\"\nspec:\n  # You may need to specify the IngressClass if your cluster does not have a\n  # default IngressClass, e.g. for Ingress NGINX Controller, uncomment the\n  # below line:\n  # ingressClassName: nginx\n  rules:\n    - host: connect.example.com\n      http:\n        paths:\n        - path: /\n          pathType: Prefix\n          backend:\n            service:\n              name: op-connect\n              port:\n                name: connect-api\n  tls:\n    - hosts:\n      - connect.example.com\n      secretName: connect-cert\n"
  },
  {
    "path": "examples/kubernetes/op-connect-issuer.yaml",
    "content": "apiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt\nspec:\n  acme:\n    # You *must* replace this value with a valid email address.\n    # Let's Encrypt will use this to contact you about expiring\n    # certificates, and issues related to your account.\n    email: user@example.com\n    server: https://acme-v02.api.letsencrypt.org/directory\n    privateKeySecretRef:\n      # Secret resource that will be used to store the account's private key.\n      name: letsencrypt-issuer-account-key\n    solvers:\n    # Use HTTP01 solver\n    - http01:\n        ingress:\n          # Edit existing Ingress\n          name: connect-ingress\n"
  },
  {
    "path": "examples/kubernetes/op-connect-service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  name: op-connect\nspec:\n  selector:\n    app: op-connect\n  ports:\n    - name: connect-api\n      port: 8080\n      targetPort: connect-api\n "
  }
]