@ossy/deployment-tools 1.21.3 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 1.22.0 (2026-05-27)
7
+
8
+
9
+ ### Features
10
+
11
+ * **authentication:** Introduce authentication feature package with sign-in, sign-up, and invitation verification pages ([579a3d5](https://github.com/ossy-se/ossy/commit/579a3d504313db04acb3239bbfd0a49bc6321884))
12
+
13
+
14
+
15
+
16
+
17
+ ## 1.21.4 (2026-05-26)
18
+
19
+ **Note:** Version bump only for package @ossy/deployment-tools
20
+
21
+
22
+
23
+
24
+
6
25
  ## 1.21.3 (2026-05-26)
7
26
 
8
27
  **Note:** Version bump only for package @ossy/deployment-tools
package/README.md CHANGED
@@ -75,9 +75,7 @@ Host ports are auto-assigned starting at `3002`, counting only HTTP services (TC
75
75
 
76
76
  #### TCP/UDP service
77
77
 
78
- No Caddy block. Containers run with **`--network host`** so game servers bind ports on the EC2 instance directly (avoids Docker bridge DNAT issues for public traffic). The `ports` and `protocol` fields still drive **security group** ingress rules they are not passed as `-p` mappings.
79
-
80
- Use this for game servers or any other raw socket protocol.
78
+ No Caddy block. Ports are mapped directly (`HOST:CONTAINER`) and opened in the EC2 security group. Use this for game servers or any other raw socket protocol.
81
79
 
82
80
  ```json
83
81
  "services": [
@@ -87,15 +85,6 @@ Use this for game servers or any other raw socket protocol.
87
85
  "image": "itzg/minecraft-server",
88
86
  "ports": [25565],
89
87
  "protocol": "tcp"
90
- },
91
- {
92
- "name": "palworld",
93
- "type": "tcp",
94
- "image": "thijsvanloef/palworld-server-docker",
95
- "ports": [
96
- { "port": 8211, "protocol": "udp" },
97
- { "port": 27021, "protocol": "udp" }
98
- ]
99
88
  }
100
89
  ]
101
90
  ```
@@ -105,8 +94,8 @@ Use this for game servers or any other raw socket protocol.
105
94
  | `name` | Unique service name — Docker container name and systemd unit. |
106
95
  | `type` | Must be `"tcp"` to enable this mode. |
107
96
  | `image` | Docker image to pull and run. |
108
- | `ports` | Port numbers (host network) and security group rules. Use a **number** with service-level `protocol`, or `{ "port": 8211, "protocol": "udp" }` per port when protocols differ. |
109
- | `protocol` | Default for numeric `ports` entries: `"tcp"` (default), `"udp"`, or `"both"`. Ignored when every port is an object with its own `protocol`. Must match what the game image listens on. |
97
+ | `ports` | Array of port numbers to expose on the host and open in the security group. |
98
+ | `protocol` | `"tcp"` (default), `"udp"`, or `"both"`. |
110
99
 
111
100
  **Fixed built-in services** (always present, not configurable via `services`):
112
101
  - `ossy-api` — the Ossy API (`ghcr.io/ossy-se/api:latest`, port 3001)
@@ -133,38 +122,6 @@ Each platform is a CDK `Stage` containing five stacks:
133
122
 
134
123
  The Elastic IP ensures the EC2 instance can be replaced (instance type changes, user data updates) without breaking DNS or CloudFormation cross-stack exports.
135
124
 
136
- ## Destroy + redeploy
137
-
138
- `<platformName>` is the `platformName` field in `packages/infrastructure/platforms.json` (CDK stage id). `<aws-profile>` is your AWS CLI profile for that account. List stacks: `npx cdk list --profile <aws-profile>`.
139
-
140
- Full platform teardown:
141
-
142
- ```bash
143
- cd packages/deployment-tools
144
- npx cdk destroy '<platformName>/**' --profile <aws-profile>
145
- ```
146
-
147
- Recreate (CDK applies stacks in dependency order):
148
-
149
- ```bash
150
- npx cdk deploy '<platformName>/**' --profile <aws-profile>
151
- ```
152
-
153
- Recycle **only the EC2 host** (keep S3, CDN, and backups):
154
-
155
- ```bash
156
- npx cdk destroy '<platformName>/dns' --profile <aws-profile>
157
- npx cdk destroy '<platformName>/deployment-target' --profile <aws-profile>
158
- npx cdk deploy '<platformName>/deployment-target' --profile <aws-profile>
159
- npx cdk deploy '<platformName>/dns' --profile <aws-profile>
160
- ```
161
-
162
- Do not destroy `storage-static` unless you intend to drop media storage (bucket is `RETAIN` and may remain in the account anyway).
163
-
164
- The S3 bucket and **AWS Backup vault** use `RETAIN` so media and recovery points survive a stack delete. CloudFront and the backup *plan* are removed with the stack.
165
-
166
- If deploy fails with **BackupVault already exists** after a full destroy, a retained vault is still in the account (`<platformName>-static-media-backup`). Delete it in AWS Backup (empty recovery points first), or import it into the new stack.
167
-
168
125
  ## Bootstrapping a new platform
169
126
 
170
127
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/deployment-tools",
3
- "version": "1.21.3",
3
+ "version": "1.22.0",
4
4
  "description": "Collection of scripts and tools to aid deployment of containers and static files to Amazon Web Services through GitHub Actions",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -16,11 +16,11 @@
16
16
  "aws-cdk": "^2.73.0",
17
17
  "aws-cdk-lib": "^2.73.0",
18
18
  "constructs": "^10.1.304",
19
- "glob": "^9.3.2",
20
- "nanoid": "^3.3.4"
19
+ "glob": "^13.0.6",
20
+ "nanoid": "^5.1.11"
21
21
  },
22
22
  "devDependencies": {
23
- "jest": "^27.5.1"
23
+ "jest": "^30.4.2"
24
24
  },
25
- "gitHead": "909e3d78752d07afceb60af78784987f6fcea8b7"
25
+ "gitHead": "a77633cb70c49870fb1428ca563244d4b52f2edc"
26
26
  }
@@ -17,8 +17,8 @@
17
17
  * @property {'tcp'} type - marks a raw socket service; bypasses Caddy entirely
18
18
  * @property {string} name - unique service name (container + systemd unit)
19
19
  * @property {string} image - Docker image (e.g. itzg/minecraft-server)
20
- * @property {(number|{port: number, protocol: 'tcp'|'udp'|'both'})[]} ports - host ports + security group rules
21
- * @property {'tcp'|'udp'|'both'} [protocol] - default for numeric ports; defaults to 'tcp'
20
+ * @property {number[]} ports - ports to open on the host and in the EC2 security group
21
+ * @property {'tcp'|'udp'|'both'} [protocol] - defaults to 'tcp'
22
22
  * @property {Record<string,string>=} env - environment variables written to /etc/environment on the EC2 host at boot time; never uploaded to S3
23
23
  *
24
24
  * @property {string} awsAccountId - Aws account id
@@ -11,38 +11,14 @@
11
11
  * @property {'tcp'} type - marks this as a raw TCP/UDP service (no HTTP/Caddy)
12
12
  * @property {string} name - unique name, used for container name and systemd unit
13
13
  * @property {string} image - Docker image to run
14
- * @property {(number|TcpPortEntry)[]} ports - host ports; each entry uses `protocol` or the service default
15
- * @property {'tcp'|'udp'|'both'} [protocol] - default for numeric ports; defaults to 'tcp'
16
- *
17
- * @typedef {Object} TcpPortEntry
18
- * @property {number} port
19
- * @property {'tcp'|'udp'|'both'} protocol
14
+ * @property {number[]} ports - ports to expose directly (same inside and outside container)
15
+ * @property {'tcp'|'udp'|'both'} [protocol] - defaults to 'tcp'
20
16
  *
21
17
  * @typedef {HttpServiceConfig|TcpServiceConfig} ContainerServiceConfig
22
18
  */
23
19
 
24
20
  const BASE_HTTP_PORT = 3002 // 3000 = platform runtime, 3001 = API
25
21
 
26
- /**
27
- * @param {(number|TcpPortEntry)[]} ports
28
- * @param {'tcp'|'udp'|'both'} defaultProtocol
29
- * @returns {TcpPortEntry[]}
30
- */
31
- const normalizePortEntries = (ports, defaultProtocol = 'tcp') =>
32
- ports.map(entry =>
33
- typeof entry === 'number'
34
- ? { port: entry, protocol: defaultProtocol }
35
- : { port: entry.port, protocol: entry.protocol ?? defaultProtocol }
36
- )
37
-
38
- /** @param {TcpPortEntry} entry */
39
- const expandSecurityGroupRules = ({ port, protocol }) => {
40
- if (protocol === 'both') {
41
- return [{ port, protocol: 'tcp' }, { port, protocol: 'udp' }]
42
- }
43
- return [{ port, protocol }]
44
- }
45
-
46
22
  // ---------------------------------------------------------------------------
47
23
  // HTTP service — reverse-proxied through Caddy, container listens on port 3000
48
24
  // ---------------------------------------------------------------------------
@@ -104,23 +80,23 @@ ${this.domain} {
104
80
  }
105
81
 
106
82
  // ---------------------------------------------------------------------------
107
- // TCP/UDP service — bypasses Caddy, listens on the EC2 host network namespace.
108
- // Uses --network host so game traffic hits the process directly (no bridge DNAT).
109
- // Security group ingress still comes from securityGroupPorts below.
83
+ // TCP/UDP service — bypasses Caddy, ports exposed directly on the host.
84
+ // Used for game servers and any other raw socket services.
110
85
  // ---------------------------------------------------------------------------
111
86
 
112
87
  class TcpContainerService {
113
88
  constructor({ name, image, ports, protocol = 'tcp' }) {
114
89
  this.name = name
115
90
  this.image = image
116
- this.portEntries = normalizePortEntries(ports, protocol)
91
+ this.ports = ports
92
+ this.protocol = protocol // 'tcp' | 'udp' | 'both'
117
93
  }
118
94
 
119
95
  get systemdUnitFile() {
120
- const portList = this.portEntries.map(({ port, protocol }) => `${port}/${protocol}`).join(', ')
96
+ const portMappings = this.ports.map(p => `-p ${p}:${p}`).join(' \\\n ')
121
97
  return `
122
98
  [Unit]
123
- Description=Container service: ${this.name} (host network, ${portList})
99
+ Description=Container service: ${this.name} (${this.protocol.toUpperCase()} ${this.ports.join(', ')})
124
100
  After=network.target docker.service
125
101
  Requires=docker.service
126
102
 
@@ -129,7 +105,8 @@ EnvironmentFile=/etc/environment
129
105
  ExecStartPre=-/usr/bin/docker rm -f ${this.name}
130
106
  ExecStartPre=/usr/bin/docker pull ${this.image}
131
107
  ExecStart=/usr/bin/docker run --name ${this.name} \\
132
- --network host \\
108
+ --network ossy-network \\
109
+ ${portMappings} \\
133
110
  --env-file /etc/environment \\
134
111
  ${this.image}
135
112
  ExecStop=/usr/bin/docker stop ${this.name}
@@ -144,9 +121,12 @@ WantedBy=multi-user.target
144
121
  /** TCP services bypass Caddy — no Caddy block. */
145
122
  get caddyBlock() { return null }
146
123
 
147
- /** Each port needs an explicit security group ingress rule matching its protocol. */
124
+ /** Each port needs an explicit security group ingress rule. */
148
125
  get securityGroupPorts() {
149
- return this.portEntries.flatMap(expandSecurityGroupRules)
126
+ return this.ports.flatMap(port => {
127
+ if (this.protocol === 'both') return [{ port, protocol: 'tcp' }, { port, protocol: 'udp' }]
128
+ return [{ port, protocol: this.protocol }]
129
+ })
150
130
  }
151
131
 
152
132
  get unitName() { return `${this.name}.service` }
@@ -178,6 +158,5 @@ const fromConfig = (services = []) => {
178
158
  module.exports = {
179
159
  HttpContainerService,
180
160
  TcpContainerService,
181
- normalizePortEntries,
182
161
  fromConfig
183
162
  }
@@ -39,7 +39,6 @@ class StorageStaticStack extends Stack {
39
39
  })
40
40
 
41
41
  const backupVault = new BackupVault(this, 'StaticMediaBackupVault', {
42
- backupVaultName: `${props.config.platformName}-static-media-backup`,
43
42
  removalPolicy: RemovalPolicy.RETAIN
44
43
  })
45
44