@hackfed/schemas 0.0.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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2025 Hackfed Team & Contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Schemas • Hackfed
2
+
3
+ This repository contains JSON Schemas for Resource Definiton Schemas used in Hackfed.
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@hackfed/schemas",
3
+ "version": "0.0.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ "./*": "./src/schemas/*"
8
+ },
9
+ "files": [
10
+ "src/schemas",
11
+ "LICENSE",
12
+ "README"
13
+ ],
14
+ "devDependencies": {
15
+ "@bksp/style-guide": "^1.2.6",
16
+ "@tsconfig/strictest": "^2.0.8",
17
+ "@types/node": "^25.0.9",
18
+ "change-case": "^5.4.4",
19
+ "eslint": "^9.39.2",
20
+ "glob": "^13.0.0",
21
+ "tsx": "^4.21.0",
22
+ "typescript": "^5.9.3",
23
+ "zod": "4"
24
+ },
25
+ "peerDependencies": {
26
+ "zod": "4"
27
+ },
28
+ "scripts": {
29
+ "generate": "tsx src/cmd/generate-json-schemas.ts"
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ /* eslint-disable-next-line unicorn/no-empty-file -- this is a stub file. */
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod'
2
+
3
+ export const Inet6NumberSchema = z
4
+ .string()
5
+ .regex(
6
+ /^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$/
7
+ )
8
+ .meta({
9
+ description: 'An IPv6 address in standard notation',
10
+ examples: ['fd79:7636:1f08:883d::008', '2001:db8::1', '::1', 'fe80::1/128'],
11
+ id: 'Inet6Number',
12
+ title: 'IPv6 Address',
13
+ })
14
+
15
+ export type Inet6Number = z.infer<typeof Inet6NumberSchema>
16
+
17
+ export const __schemas = [Inet6NumberSchema]
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod'
2
+
3
+ export const OrganizationIdSchema = z
4
+ .string()
5
+ .regex(/^[a-z0-9]*$/)
6
+ .min(4)
7
+ .max(16)
8
+ .meta({
9
+ description: 'A unique identifier for an organization in the Hackfed registry',
10
+ examples: ['bksp', 'xkem'],
11
+ id: 'OrganizationId',
12
+ title: 'Organization ID',
13
+ })
14
+
15
+ export type OrganizationId = z.infer<typeof OrganizationIdSchema>
16
+
17
+ export const __schemas = [OrganizationIdSchema]
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod'
2
+
3
+ import { OrganizationIdSchema } from '../base/organization-id'
4
+
5
+ export const DirectoryExchangeSchema = z
6
+ .object({
7
+ codecs: z
8
+ .array(z.enum(['opus', 'g722', 'ulaw']))
9
+ .min(1)
10
+ .describe('Supported audio codecs'),
11
+ endpoint: z
12
+ .string()
13
+ .describe('Network address and port of the exchange (e.g., [IPv6]:port or IPv4:port)'),
14
+ id: z.string().describe('Unique exchange identifier within the organization'),
15
+ prefixes: z
16
+ .array(z.e164().describe('Telephony number prefix (e.g., country or area code)'))
17
+ .describe('Telephony number prefixes allocated to the organization for this exchange'),
18
+ protocol: z.enum(['iax2']).describe('Telephony protocol used by the exchange'),
19
+ })
20
+ .strict()
21
+
22
+ export const DirectoryOrgSchema = z
23
+ .object({
24
+ exchanges: z
25
+ .array(DirectoryExchangeSchema)
26
+ .describe('List of telephony exchanges for the organization'),
27
+ name: z.string().describe('Name of the organization'),
28
+ orgId: OrganizationIdSchema.describe('Unique identifier for the organization'),
29
+ })
30
+ .strict()
31
+
32
+ export const TelephonyDirectorySchema = z
33
+ .object({
34
+ orgs: z
35
+ .array(DirectoryOrgSchema)
36
+ .describe('List of organizations participating in Hackfed Telephony Network.'),
37
+ })
38
+ .meta({
39
+ description: 'Aggregated telephony service information for Hackfed network members.',
40
+ id: 'TelephonyDirectory',
41
+ title: 'Hackfed Telephony Directory',
42
+ })
43
+
44
+ export type TelephonyDirectory = z.infer<typeof TelephonyDirectorySchema>
45
+ export type DirectoryOrg = z.infer<typeof DirectoryOrgSchema>
46
+ export type DirectoryExchange = z.infer<typeof DirectoryExchangeSchema>
47
+
48
+ export const __schemas = [TelephonyDirectorySchema]
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod'
2
+
3
+ import { OrganizationIdSchema } from '../base/organization-id'
4
+ import { AboutServiceSchema } from '../service/about'
5
+ import { AccessServiceSchema } from '../service/access'
6
+ import { NebulaServiceSchema } from '../service/nebula'
7
+ import { TelephonyServiceSchema } from '../service/telephony'
8
+
9
+ export const OrganizationSchema = z
10
+ .object({
11
+ apiVersion: z.literal('hackfed/v1').describe('API version identifier'),
12
+ kind: z.literal('Organization').describe('Resource type'),
13
+ metadata: z
14
+ .object({
15
+ orgId: OrganizationIdSchema.describe('Identifier of the organization owning this resource'),
16
+ })
17
+ .describe('Metadata about the organization'),
18
+ spec: z
19
+ .object({
20
+ id: OrganizationIdSchema.describe('Organization ID (must match metadata.orgId)'),
21
+ name: z
22
+ .string()
23
+ .min(4)
24
+ .max(64)
25
+ .describe('Human-readable organization name'),
26
+ services: z
27
+ .object({
28
+ about: AboutServiceSchema.optional(),
29
+ access: AccessServiceSchema.optional(),
30
+ nebula: NebulaServiceSchema.optional(),
31
+ telephony: TelephonyServiceSchema.optional(),
32
+ })
33
+ .describe('Services provided by the organization')
34
+ .optional(),
35
+ })
36
+ .strict()
37
+ .describe('Organization specification'),
38
+ })
39
+ .meta({
40
+ description: 'Complete organization entity definition for Hackfed registry',
41
+ id: 'Organization',
42
+ title: 'Hackfed Organization',
43
+ })
44
+
45
+ export type Organization = z.infer<typeof OrganizationSchema>
46
+
47
+ export const __schemas = [OrganizationSchema]
@@ -0,0 +1,23 @@
1
+ import { z } from 'zod'
2
+
3
+ export const AboutServiceSchema = z
4
+ .object({
5
+ contact: z
6
+ .object({
7
+ email: z.email().describe("Organization's contact email address").optional(),
8
+ website: z.url().describe("Organization's website URL").optional(),
9
+ })
10
+ .strict()
11
+ .describe('Contact information for the organization')
12
+ .optional(),
13
+ })
14
+ .strict()
15
+ .meta({
16
+ description: 'Contact information and general details about an organization',
17
+ id: 'AboutService',
18
+ title: 'Organization About Service',
19
+ })
20
+
21
+ export type AboutService = z.infer<typeof AboutServiceSchema>
22
+
23
+ export const __schemas = [AboutServiceSchema]
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod'
2
+
3
+ export const GithubMaintainerSchema = z
4
+ .object({
5
+ type: z.literal('github'),
6
+ username: z.string().describe('Username for GitHub type maintainers'),
7
+ })
8
+ .describe('Maintainer identified by GitHub username')
9
+
10
+ export const EmailMaintainerSchema = z
11
+ .object({
12
+ email: z.email().describe('Email address for email type maintainers'),
13
+ type: z.literal('email'),
14
+ })
15
+ .describe('Maintainer identified by email address')
16
+
17
+ export const MaintainerSchema = z.discriminatedUnion('type', [
18
+ GithubMaintainerSchema,
19
+ EmailMaintainerSchema,
20
+ ])
21
+
22
+ export const AccessServiceSchema = z
23
+ .object({
24
+ maintainers: z
25
+ .array(MaintainerSchema)
26
+ .min(1)
27
+ .describe('List of organization maintainers')
28
+ .optional(),
29
+ })
30
+ .strict()
31
+ .meta({
32
+ description: 'Access control and maintainer information for an organization',
33
+ id: 'AccessService',
34
+ title: 'Organization Access Service',
35
+ })
36
+
37
+ export type AccessService = z.infer<typeof AccessServiceSchema>
38
+ export type Maintainer = z.infer<typeof MaintainerSchema>
39
+ export type GithubMaintainer = z.infer<typeof GithubMaintainerSchema>
40
+ export type EmailMaintainer = z.infer<typeof EmailMaintainerSchema>
41
+
42
+ export const __schemas = [AccessServiceSchema]
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod'
2
+
3
+ import { Inet6NumberSchema } from '../base/inet6-number'
4
+
5
+ export const NebulaNodeSchema = z
6
+ .object({
7
+ address: Inet6NumberSchema.describe('IPv6 address of the Nebula node'),
8
+ certificates: z
9
+ .array(
10
+ z
11
+ .string()
12
+ .regex(/^[a-f0-9]{64}$/)
13
+ .describe('SHA-256 certificate fingerprint in hexadecimal')
14
+ )
15
+ .min(1)
16
+ .describe('List of certificate fingerprints for the node')
17
+ .optional(),
18
+ lighthouse: z
19
+ .object({
20
+ enabled: z.boolean().describe('Whether this node acts as a lighthouse'),
21
+ endpoints: z
22
+ .array(z.string().describe('Endpoint in format host:port'))
23
+ .min(1)
24
+ .describe('Public endpoints for lighthouse connectivity')
25
+ .optional(),
26
+ })
27
+ .strict()
28
+ .describe('Lighthouse configuration for the node')
29
+ .optional(),
30
+ })
31
+ .strict()
32
+
33
+ export const NebulaServiceSchema = z.array(NebulaNodeSchema).meta({
34
+ description: 'Nebula VPN network configuration for the organization',
35
+ id: 'NebulaService',
36
+ title: 'Organization Nebula Service',
37
+ })
38
+
39
+ export type NebulaService = z.infer<typeof NebulaServiceSchema>
40
+ export type NebulaNode = z.infer<typeof NebulaNodeSchema>
41
+
42
+ export const __schemas = [NebulaServiceSchema]
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod'
2
+
3
+ export const ExchangeSchema = z
4
+ .object({
5
+ address: z
6
+ .string()
7
+ .describe('Network address and port of the exchange (e.g., [IPv6]:port or IPv4:port)'),
8
+ codecs: z
9
+ .array(z.enum(['opus', 'g722', 'ulaw']))
10
+ .min(1)
11
+ .describe('Supported audio codecs'),
12
+ id: z.string().describe('Unique identifier for the exchange'),
13
+ protocol: z.enum(['iax2']).describe('Telephony protocol used by the exchange'),
14
+ })
15
+ .strict()
16
+
17
+ export const PrefixSchema = z
18
+ .object({
19
+ description: z.string().describe('Human-readable description of the prefix').optional(),
20
+ exchange: z.string().describe('Reference to the exchange ID handling this prefix'),
21
+ id: z.string().describe('Unique identifier for the prefix'),
22
+ prefix: z.e164().describe('Numeric prefix for phone numbers'),
23
+ })
24
+ .strict()
25
+
26
+ export const PhonebookSchema = z
27
+ .object({
28
+ format: z.enum(['hackfed']).describe('Format of the phonebook data'),
29
+ url: z.url().describe('URL to the phonebook resource'),
30
+ })
31
+ .strict()
32
+
33
+ export const TelephonyServiceSchema = z
34
+ .object({
35
+ exchanges: z.array(ExchangeSchema).describe('List of telephony exchanges').optional(),
36
+ phonebook: z.array(PhonebookSchema).describe('Public phonebook URLs').optional(),
37
+ prefixes: z
38
+ .array(PrefixSchema)
39
+ .describe('Telephony number prefixes allocated to the organization')
40
+ .optional(),
41
+ })
42
+ .strict()
43
+ .meta({
44
+ description: 'Telephony configuration including exchanges, prefixes, and phonebook',
45
+ id: 'TelephonyService',
46
+ title: 'Organization Telephony Service',
47
+ })
48
+
49
+ export type TelephonyService = z.infer<typeof TelephonyServiceSchema>
50
+ export type Exchange = z.infer<typeof ExchangeSchema>
51
+ export type Prefix = z.infer<typeof PrefixSchema>
52
+ export type Phonebook = z.infer<typeof PhonebookSchema>
53
+
54
+ export const __schemas = [TelephonyServiceSchema]