Changes
21 changed files (+693/-3)
-
REUSE.toml (new)
-
@@ -0,0 +1,6 @@version = 1 [[annotations]] path = "web/auth/bindings/**/*.go" SPDX-FileCopyrightText = "Copyright 2026 Shota FUJI" SPDX-License-Identifier = "AGPL-3.0-only"
-
-
-
@@ -46,7 +46,7 @@rec { packages = forEachSystems ( { pkgs, ... }: { auth-default = pkgs.callPackage ./auth { }; web = pkgs.callPackage ./web { }; demo = pkgs.callPackage ./demo { }; docs = pkgs.callPackage ./docs { }; }
-
@@ -115,8 +115,12 @@# > Zig LSP implementation + Zig Language Server # https://github.com/zigtools/zls zls_0_16 # > Official language server for the Go language # https://github.com/golang/tools/tree/master/gopls gopls ] ++ packages.${system}.auth-default.nativeBuildInputs ++ packages.${system}.web.nativeBuildInputs ++ packages.${system}.demo.nativeBuildInputs ++ packages.${system}.docs.nativeBuildInputs; };
-
-
-
-
@@ -0,0 +1,114 @@// Code generated by the FlatBuffers compiler. DO NOT EDIT. package bindings import ( flatbuffers "github.com/google/flatbuffers/go" ) /// 認可用 WebAssembly 関数に渡されるデータ type AccessRequest struct { _tab flatbuffers.Table } func GetRootAsAccessRequest(buf []byte, offset flatbuffers.UOffsetT) *AccessRequest { n := flatbuffers.GetUOffsetT(buf[offset:]) x := &AccessRequest{} x.Init(buf, n+offset) return x } func FinishAccessRequestBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.Finish(offset) } func GetSizePrefixedRootAsAccessRequest(buf []byte, offset flatbuffers.UOffsetT) *AccessRequest { n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) x := &AccessRequest{} x.Init(buf, n+offset+flatbuffers.SizeUint32) return x } func FinishSizePrefixedAccessRequestBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.FinishSizePrefixed(offset) } func (rcv *AccessRequest) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i } func (rcv *AccessRequest) Table() flatbuffers.Table { return rcv._tab } /// アクセスを要求しているユーザ func (rcv *AccessRequest) User(obj *User) *User { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { x := rcv._tab.Indirect(o + rcv._tab.Pos) if obj == nil { obj = new(User) } obj.Init(rcv._tab.Bytes, x) return obj } return nil } /// アクセスを要求しているユーザ /// アクセスの種類 func (rcv *AccessRequest) AccessType() ResourceAccessType { o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) if o != 0 { return ResourceAccessType(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 0 } /// アクセスの種類 func (rcv *AccessRequest) MutateAccessType(n ResourceAccessType) bool { return rcv._tab.MutateByteSlot(6, byte(n)) } func (rcv *AccessRequest) ResourceType() Resource { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { return Resource(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 0 } func (rcv *AccessRequest) MutateResourceType(n Resource) bool { return rcv._tab.MutateByteSlot(8, byte(n)) } /// アクセスしようとしているリソース func (rcv *AccessRequest) Resource(obj *flatbuffers.Table) bool { o := flatbuffers.UOffsetT(rcv._tab.Offset(10)) if o != 0 { rcv._tab.Union(obj, o) return true } return false } /// アクセスしようとしているリソース func AccessRequestStart(builder *flatbuffers.Builder) { builder.StartObject(4) } func AccessRequestAddUser(builder *flatbuffers.Builder, user flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(user), 0) } func AccessRequestAddAccessType(builder *flatbuffers.Builder, accessType ResourceAccessType) { builder.PrependByteSlot(1, byte(accessType), 0) } func AccessRequestAddResourceType(builder *flatbuffers.Builder, resourceType Resource) { builder.PrependByteSlot(2, byte(resourceType), 0) } func AccessRequestAddResource(builder *flatbuffers.Builder, resource flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(3, flatbuffers.UOffsetT(resource), 0) } func AccessRequestEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
-
-
-
@@ -0,0 +1,35 @@// Code generated by the FlatBuffers compiler. DO NOT EDIT. package bindings import "strconv" type Resource byte const ( ResourceNONE Resource = 0 ResourceUser Resource = 1 ResourceUserAttribute Resource = 2 ResourceUserAttributeDefinition Resource = 3 ) var EnumNamesResource = map[Resource]string{ ResourceNONE: "NONE", ResourceUser: "User", ResourceUserAttribute: "UserAttribute", ResourceUserAttributeDefinition: "UserAttributeDefinition", } var EnumValuesResource = map[string]Resource{ "NONE": ResourceNONE, "User": ResourceUser, "UserAttribute": ResourceUserAttribute, "UserAttributeDefinition": ResourceUserAttributeDefinition, } func (v Resource) String() string { if s, ok := EnumNamesResource[v]; ok { return s } return "Resource(" + strconv.FormatInt(int64(v), 10) + ")" }
-
-
-
@@ -0,0 +1,42 @@// Code generated by the FlatBuffers compiler. DO NOT EDIT. package bindings import "strconv" /// リソースに対するアクセスの種類 type ResourceAccessType byte const ( /// 上書きや削除といった既存のデータに対する変更 ResourceAccessTypeModify ResourceAccessType = 0 /// 追加・登録 /// /// 既存の同種リソースに対して変更は発生しない。同種の他のリソースに対して /// 変更が必要な場合は `Modify` のリソース変更も同時に認可を行う。 ResourceAccessTypeCreate ResourceAccessType = 1 /// 読み取り /// /// 一覧取得といったアクセスは個別のリソースに対してそれぞれ `Read` の /// 認可が行われる。 ResourceAccessTypeRead ResourceAccessType = 2 ) var EnumNamesResourceAccessType = map[ResourceAccessType]string{ ResourceAccessTypeModify: "Modify", ResourceAccessTypeCreate: "Create", ResourceAccessTypeRead: "Read", } var EnumValuesResourceAccessType = map[string]ResourceAccessType{ "Modify": ResourceAccessTypeModify, "Create": ResourceAccessTypeCreate, "Read": ResourceAccessTypeRead, } func (v ResourceAccessType) String() string { if s, ok := EnumNamesResourceAccessType[v]; ok { return s } return "ResourceAccessType(" + strconv.FormatInt(int64(v), 10) + ")" }
-
-
-
@@ -0,0 +1,130 @@// Code generated by the FlatBuffers compiler. DO NOT EDIT. package bindings import ( flatbuffers "github.com/google/flatbuffers/go" ) /// システムに登録されているログイン可能なユーザ type User struct { _tab flatbuffers.Table } func GetRootAsUser(buf []byte, offset flatbuffers.UOffsetT) *User { n := flatbuffers.GetUOffsetT(buf[offset:]) x := &User{} x.Init(buf, n+offset) return x } func FinishUserBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.Finish(offset) } func GetSizePrefixedRootAsUser(buf []byte, offset flatbuffers.UOffsetT) *User { n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) x := &User{} x.Init(buf, n+offset+flatbuffers.SizeUint32) return x } func FinishSizePrefixedUserBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.FinishSizePrefixed(offset) } func (rcv *User) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i } func (rcv *User) Table() flatbuffers.Table { return rcv._tab } /// システムによって自動発番された ID /// /// 同一判定にはこのフィールドを利用すること。値は不変である。 func (rcv *User) Id() []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { return rcv._tab.ByteVector(o + rcv._tab.Pos) } return nil } /// システムによって自動発番された ID /// /// 同一判定にはこのフィールドを利用すること。値は不変である。 /// 管理者かどうか /// /// `attributes` でも表現できる属性ではあるが、ベクターを毎回走査するパフォーマンス /// ペナルティを避けるために設置している。ハンドラ内でまずこのフィールドを確認し、 /// `true` の場合にアクセスを許可すれば「誰もアクセスできないデータがある」状態を /// 避けることができる。 /// /// サーバにあるデータベースファイルにアクセスできる人物は実質全てのアクセス権限を /// 持つに等しい。ただ、意図しない変更を予防する等の理由でアクセスを制限することにも /// 長所があるため、組織のリスクマネジメント方針に基づいて決めることを推奨。 func (rcv *User) IsSuperUser() bool { o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) if o != 0 { return rcv._tab.GetBool(o + rcv._tab.Pos) } return false } /// 管理者かどうか /// /// `attributes` でも表現できる属性ではあるが、ベクターを毎回走査するパフォーマンス /// ペナルティを避けるために設置している。ハンドラ内でまずこのフィールドを確認し、 /// `true` の場合にアクセスを許可すれば「誰もアクセスできないデータがある」状態を /// 避けることができる。 /// /// サーバにあるデータベースファイルにアクセスできる人物は実質全てのアクセス権限を /// 持つに等しい。ただ、意図しない変更を予防する等の理由でアクセスを制限することにも /// 長所があるため、組織のリスクマネジメント方針に基づいて決めることを推奨。 func (rcv *User) MutateIsSuperUser(n bool) bool { return rcv._tab.MutateBoolSlot(6, n) } /// ワークスペース固有のカスタム属性一覧 func (rcv *User) Attributes(obj *UserAttribute, j int) bool { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { x := rcv._tab.Vector(o) x += flatbuffers.UOffsetT(j) * 4 x = rcv._tab.Indirect(x) obj.Init(rcv._tab.Bytes, x) return true } return false } func (rcv *User) AttributesLength() int { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { return rcv._tab.VectorLen(o) } return 0 } /// ワークスペース固有のカスタム属性一覧 func UserStart(builder *flatbuffers.Builder) { builder.StartObject(3) } func UserAddId(builder *flatbuffers.Builder, id flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(id), 0) } func UserAddIsSuperUser(builder *flatbuffers.Builder, isSuperUser bool) { builder.PrependBoolSlot(1, isSuperUser, false) } func UserAddAttributes(builder *flatbuffers.Builder, attributes flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(2, flatbuffers.UOffsetT(attributes), 0) } func UserStartAttributesVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(4, numElems, 4) } func UserEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
-
-
-
@@ -0,0 +1,76 @@// Code generated by the FlatBuffers compiler. DO NOT EDIT. package bindings import ( flatbuffers "github.com/google/flatbuffers/go" ) /// ユーザに設定されたカスタム属性 type UserAttribute struct { _tab flatbuffers.Table } func GetRootAsUserAttribute(buf []byte, offset flatbuffers.UOffsetT) *UserAttribute { n := flatbuffers.GetUOffsetT(buf[offset:]) x := &UserAttribute{} x.Init(buf, n+offset) return x } func FinishUserAttributeBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.Finish(offset) } func GetSizePrefixedRootAsUserAttribute(buf []byte, offset flatbuffers.UOffsetT) *UserAttribute { n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) x := &UserAttribute{} x.Init(buf, n+offset+flatbuffers.SizeUint32) return x } func FinishSizePrefixedUserAttributeBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.FinishSizePrefixed(offset) } func (rcv *UserAttribute) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i } func (rcv *UserAttribute) Table() flatbuffers.Table { return rcv._tab } /// 該当する `UserAttributeDefinition` の `key` func (rcv *UserAttribute) DefinitionKey() []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { return rcv._tab.ByteVector(o + rcv._tab.Pos) } return nil } /// 該当する `UserAttributeDefinition` の `key` /// 選択肢のキー func (rcv *UserAttribute) ChoiceKey() []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) if o != 0 { return rcv._tab.ByteVector(o + rcv._tab.Pos) } return nil } /// 選択肢のキー func UserAttributeStart(builder *flatbuffers.Builder) { builder.StartObject(2) } func UserAttributeAddDefinitionKey(builder *flatbuffers.Builder, definitionKey flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(definitionKey), 0) } func UserAttributeAddChoiceKey(builder *flatbuffers.Builder, choiceKey flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(choiceKey), 0) } func UserAttributeEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
-
-
-
@@ -0,0 +1,73 @@// Code generated by the FlatBuffers compiler. DO NOT EDIT. package bindings import ( flatbuffers "github.com/google/flatbuffers/go" ) /// ワークスペース固有のカスタム属性定義 /// /// この定義を基に各ユーザにカスタム属性が定義できる。表示名の変更や半角・全角問題 /// などに対応するため、自由入力はできず事前に定義された選択肢の中から選ぶ。 /// /// また「ユーザ情報変更はできるが一部のカスタム属性にアクセスできない」といった /// ケースを考慮し、全てのカスタム属性は任意選択とする。 type UserAttributeDefinition struct { _tab flatbuffers.Table } func GetRootAsUserAttributeDefinition(buf []byte, offset flatbuffers.UOffsetT) *UserAttributeDefinition { n := flatbuffers.GetUOffsetT(buf[offset:]) x := &UserAttributeDefinition{} x.Init(buf, n+offset) return x } func FinishUserAttributeDefinitionBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.Finish(offset) } func GetSizePrefixedRootAsUserAttributeDefinition(buf []byte, offset flatbuffers.UOffsetT) *UserAttributeDefinition { n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) x := &UserAttributeDefinition{} x.Init(buf, n+offset+flatbuffers.SizeUint32) return x } func FinishSizePrefixedUserAttributeDefinitionBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { builder.FinishSizePrefixed(offset) } func (rcv *UserAttributeDefinition) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i } func (rcv *UserAttributeDefinition) Table() flatbuffers.Table { return rcv._tab } /// ユーザ・組織が設定する、認可や一意性確保のための識別キー /// /// ユーザによって値が変更される場合がある。 func (rcv *UserAttributeDefinition) Key() []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { return rcv._tab.ByteVector(o + rcv._tab.Pos) } return nil } /// ユーザ・組織が設定する、認可や一意性確保のための識別キー /// /// ユーザによって値が変更される場合がある。 func UserAttributeDefinitionStart(builder *flatbuffers.Builder) { builder.StartObject(1) } func UserAttributeDefinitionAddKey(builder *flatbuffers.Builder, key flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(key), 0) } func UserAttributeDefinitionEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
-
-
-
-
web/auth/codegen.go (new)
-
@@ -0,0 +1,17 @@// SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026 Shota FUJI // // This program is free software: you can redistribute it and/or modify it under the terms // of the GNU Affero General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A // PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License along // with this program. If not, see <http://www.gnu.org/licenses/>. package auth //go:generate flatc --go v1.fbs --go-namespace bindings
-
-
-
web/auth/default_auth.go (new)
-
@@ -0,0 +1,22 @@// SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026 Shota FUJI // // This program is free software: you can redistribute it and/or modify it under the terms // of the GNU Affero General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A // PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License along // with this program. If not, see <http://www.gnu.org/licenses/>. package auth import ( _ "embed" ) //go:embed zig-out/bin/yamori_default_auth.wasm var DefaultWASM []byte
-
-
-
@@ -27,7 +27,7 @@ export fn v1_allow_resource_access(buffer_ptr: [*]const u8, buffer_len: u32) boo} fn v1AllowResourceAccess(buffer: []const u8) !bool { const root = flatbuffers.decodeRoot(schema.AccessRequest, @alignCast(buffer)) catch return false; const root = try flatbuffers.decodeRoot(schema.AccessRequest, @alignCast(buffer)); if (root.user().is_super_user()) { return true;
-
-
-
web/cmd/yamori/main.go (new)
-
@@ -0,0 +1,71 @@// SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026 Shota FUJI // // This program is free software: you can redistribute it and/or modify it under the terms // of the GNU Affero General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A // PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License along // with this program. If not, see <http://www.gnu.org/licenses/>. package main import ( "context" "fmt" "os" flatbuffers "github.com/google/flatbuffers/go" "github.com/tetratelabs/wazero" "pocka.jp/x/yamori/web/auth" "pocka.jp/x/yamori/web/auth/bindings" ) func main() { ctx := context.Background() r := wazero.NewRuntime(ctx) defer r.Close(ctx) msg := newMsg() mod, _ := r.Instantiate(ctx, auth.DefaultWASM) if !mod.Memory().Write(0, msg) { fmt.Printf("Memory.Write failed\n") os.Exit(1) } res, err := mod.ExportedFunction("v1_allow_resource_access").Call(ctx, 0, uint64(len(msg))) if err != nil { fmt.Printf("ACL module execution failed: %s\n", err) os.Exit(1) } fmt.Printf("Allow = %t\n", res[0] != 0) } func newMsg() []byte { builder := flatbuffers.NewBuilder(1024) fooID := builder.CreateString("foo") bindings.UserStart(builder) bindings.UserAddId(builder, fooID) bindings.UserAddIsSuperUser(builder, true) foo := bindings.UserEnd(builder) bindings.AccessRequestStart(builder) bindings.AccessRequestAddUser(builder, foo) bindings.AccessRequestAddAccessType(builder, bindings.ResourceAccessTypeModify) bindings.AccessRequestAddResourceType(builder, bindings.ResourceUser) bindings.AccessRequestAddResource(builder, foo) req := bindings.AccessRequestEnd(builder) builder.Finish(req) return builder.FinishedBytes() }
-
-
web/default.nix (new)
-
@@ -0,0 +1,57 @@# SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026 Shota FUJI # # This program is free software: you can redistribute it and/or modify it under the terms # of the GNU Affero General Public License as published by the Free Software Foundation, # either version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. { buildGoModule, src, zig_0_16, flatbuffers, lib, runCommand, callPackage, }: buildGoModule { pname = "yamori"; version = "0.1.0"; src = with lib.fileset; toSource { root = ./.; fileset = unions [ ./auth ./cmd ./go.mod ./go.sum ]; }; vendorHash = "sha256-TRzFIodz0xf1GwQUXG5YHoPs+3QcEULXrYEIktM8eto="; nativeBuildInputs = [ zig_0_16 flatbuffers ]; postConfigure = let auth-wasm = callPackage ./auth { }; in '' mkdir -p auth/zig-out/bin # go:embed does not support symlinks cp ${auth-wasm}/bin/yamori_default_auth.wasm auth/zig-out/bin/yamori_default_auth.wasm ''; }
-
-
web/go.mod (new)
-
@@ -0,0 +1,24 @@// SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026 Shota FUJI // // This program is free software: you can redistribute it and/or modify it under the terms // of the GNU Affero General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A // PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License along // with this program. If not, see <http://www.gnu.org/licenses/>. module pocka.jp/x/yamori/web go 1.26.3 require ( github.com/google/flatbuffers v25.12.19+incompatible github.com/tetratelabs/wazero v1.12.0 ) require golang.org/x/sys v0.44.0 // indirect
-
-
web/go.sum (new)
-
@@ -0,0 +1,6 @@github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU= github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-
-
web/go.sum.license (new)
-
@@ -0,0 +1,13 @@SPDX-License-Identifier: AGPL-3.0-only Copyright 2026 Shota FUJI This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
-