This is an automated email from the ASF dual-hosted git repository.

leekeiabstraction pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git


The following commit(s) were added to refs/heads/main by this push:
     new 69eebd0fc feat: [FLUSS-2686] Add COS filesystem support (#2836)
69eebd0fc is described below

commit 69eebd0fc456662327465475e7f6e2871043d9ea
Author: ForwardXu <[email protected]>
AuthorDate: Fri May 29 14:23:51 2026 +0800

    feat: [FLUSS-2686] Add COS filesystem support (#2836)
    
    * [FLUSS-2686] Add COS filesystem support
    
    This adds a new fluss-fs-cos module that integrates Tencent Cloud COS
    (Cloud Object Storage) as a remote filesystem for Fluss.
    
    Key changes:
    - Add fluss-fs-cos module with hadoop-cos and cos_api dependencies
    - Implement COSFileSystem extending HadoopFileSystem (scheme: cosn)
    - Implement COSFileSystemPlugin as FileSystemPlugin SPI
    - Add security token support (COSSecurityTokenProvider/Receiver)
    - Add DynamicTemporaryCOSCredentialsProvider for temporary credentials
    - Add integration tests for COS filesystem behavior
    - Register module in fluss-filesystems parent pom
    
    * [FLUSS-2686] Add COS filesystem support
    
    * fix some comments
    
    * fix some comments
    
    * Improve COS filesystem: fix STS token provider, add temporary credentials 
doc, and address review comments
    
    * Fix parent POM version and update config
    
    * [fluss-fs-cos] Fix NOTICE file: update outdated dependencies and add 
missing ones
    
    * [filesystem] Address review comments for COS filesystem
    
    - Rename Maven property fs.cosn.sdk.version to fs.hadoop.cos.version to 
accurately reflect that it is the hadoop-cos version rather than the Tencent 
Cloud COS SDK version.
    
    - Align COSSecurityTokenProvider default token duration with OSS/OBS (30min 
-> 1h).
    
    - Add COSWithCredentialsProviderFileSystemBehaviorITCase to cover the 
CREDENTIALS_PROVIDER branch in COSFileSystemPlugin, reusing hadoop-cos's 
EnvironmentVariableCredentialsProvider.
    
    * [cos] Rename COSN_ENDPOINT to COSN_ENDPOINT_SUFFIX for consistency with 
config key
    
    * [filesystems] Address COS review: scope STS policy, drop duplicate COS 
SDK, fix docs
    
    1. STS tokens scoped to remote.data.dir bucket: COSSecurityTokenProvider 
now derives a default policy that limits name/cos:* to the bucket (and prefix) 
parsed from fsUri. Users can override via fs.cosn.security.token.policy.
    
    2. Drop duplicate COS SDK: exclude com.qcloud:cos_api-bundle from 
hadoop-cos so only the directly declared cos_api remains on the classpath, 
eliminating 902 overlapping com.qcloud.cos.* classes during shade. NOTICE 
updated accordingly.
    
    3. Docs: use the correct hadoop-cos provider class 
EnvironmentVariableCredentialsProvider and the correct env vars COSN_SECRET_ID 
/ COSN_SECRET_KEY.
    
    4. Docs: align temporary credential lifetime with the code (1 hour, matches 
DEFAULT_DURATION_SECONDS=3600L).
    
    5. Docs: remove trailing whitespace in 
website/docs/maintenance/filesystems/cos.md.
---
 fluss-filesystems/fluss-fs-cos/pom.xml             | 273 +++++++++++++++++++++
 .../org/apache/fluss/fs/cos/COSFileSystem.java     |  67 +++++
 .../apache/fluss/fs/cos/COSFileSystemPlugin.java   | 144 +++++++++++
 .../fs/cos/token/COSSecurityTokenProvider.java     | 193 +++++++++++++++
 .../fs/cos/token/COSSecurityTokenReceiver.java     | 112 +++++++++
 .../DynamicTemporaryCOSCredentialsProvider.java    |  69 ++++++
 .../src/main/resources/META-INF/NOTICE             |  77 ++++++
 .../main/resources/META-INF/licenses/LICENSE.jaxb  | 135 ++++++++++
 .../services/org.apache.fluss.fs.FileSystemPlugin  |  17 ++
 ...org.apache.fluss.fs.token.SecurityTokenReceiver |  17 ++
 .../fluss/fs/cos/COSFileSystemBehaviorITCase.java  |  67 +++++
 .../apache/fluss/fs/cos/COSTestCredentials.java    | 119 +++++++++
 ...redentialsProviderFileSystemBehaviorITCase.java |  68 +++++
 .../COSWithTokenFileSystemBehaviorBaseITCase.java  |  43 ++++
 .../cos/COSWithTokenFileSystemBehaviorITCase.java  |  72 ++++++
 fluss-filesystems/pom.xml                          |   1 +
 fluss-test-coverage/pom.xml                        |   1 +
 website/docs/maintenance/filesystems/cos.md        |  89 +++++++
 website/docs/maintenance/filesystems/overview.md   |   4 +-
 19 files changed, 1567 insertions(+), 1 deletion(-)

diff --git a/fluss-filesystems/fluss-fs-cos/pom.xml 
b/fluss-filesystems/fluss-fs-cos/pom.xml
new file mode 100644
index 000000000..7887ee2a1
--- /dev/null
+++ b/fluss-filesystems/fluss-fs-cos/pom.xml
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.fluss</groupId>
+        <artifactId>fluss-filesystems</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>fluss-fs-cos</artifactId>
+    <name>Fluss : FileSystems : COS FS</name>
+
+    <properties>
+        <fs.hadoop.cos.version>3.3.5</fs.hadoop.cos.version>
+        <fs.cos.api.version>5.6.139</fs.cos.api.version>
+        <fs.cos.sts.sdk.version>3.1.678</fs.cos.sts.sdk.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.fluss</groupId>
+            <artifactId>fluss-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.fluss</groupId>
+            <artifactId>fluss-fs-hadoop-shaded</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.fluss</groupId>
+            <artifactId>fluss-fs-hadoop</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-cos</artifactId>
+            <version>${fs.hadoop.cos.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.qcloud</groupId>
+                    <artifactId>cos_api</artifactId>
+                </exclusion>
+                <!--
+                  Exclude the bundled COS SDK shipped by hadoop-cos to avoid 
having two
+                  COS SDK variants on the classpath (cos_api-bundle:5.6.69 and 
the directly
+                  declared cos_api:${fs.cos.api.version}), which produces 
hundreds of
+                  overlapping com.qcloud.cos.* classes during shading.
+                -->
+                <exclusion>
+                    <groupId>com.qcloud</groupId>
+                    <artifactId>cos_api-bundle</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.hadoop</groupId>
+                    <artifactId>hadoop-common</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>ch.qos.reload4j</groupId>
+                    <artifactId>reload4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-reload4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+            <version>${fs.cos.api.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.xml.bind</groupId>
+                    <artifactId>jaxb-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-sts</artifactId>
+            <version>${fs.cos.sts.sdk.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.xml.bind</groupId>
+                    <artifactId>jaxb-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <!-- Hadoop requires jaxb-api for javax.xml.bind.JAXBException -->
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>${jaxb.api.version}</version>
+            <!-- packaged as an optional dependency that is only accessible on 
Java 11+ -->
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.fluss</groupId>
+            <artifactId>fluss-test-utils</artifactId>
+        </dependency>
+        <!-- for the behavior test suite -->
+        <dependency>
+            <groupId>org.apache.fluss</groupId>
+            <artifactId>fluss-common</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>default-jar</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>test-jar</id>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <archive>
+                        <manifestEntries>
+                            <!-- jaxb-api is packaged as an optional 
dependency that is only accessible on Java 11 -->
+                            <Multi-Release>true</Multi-Release>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-javax-jars</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <artifactItems>
+                        <artifactItem>
+                            <groupId>javax.xml.bind</groupId>
+                            <artifactId>jaxb-api</artifactId>
+                            <version>${jaxb.api.version}</version>
+                            <type>jar</type>
+                            <overWrite>true</overWrite>
+                        </artifactItem>
+                    </artifactItems>
+                    
<outputDirectory>${project.build.directory}/temporary</outputDirectory>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack-javax-libraries</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <target>
+                                <echo message="unpacking javax jars"/>
+                                <unzip 
dest="${project.build.directory}/classes/META-INF/versions/11">
+                                    <fileset 
dir="${project.build.directory}/temporary">
+                                        <include name="*"/>
+                                    </fileset>
+                                </unzip>
+                            </target>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>shade-fluss</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>*:*</include>
+                                </includes>
+                                <excludes>
+                                    
<exclude>javax.servlet:servlet-api</exclude>
+                                    <exclude>xmlenc:xmlenc</exclude>
+                                </excludes>
+                            </artifactSet>
+                            <filters>
+                                <filter>
+                                    <artifact>*</artifact>
+                                    <excludes>
+                                        <exclude>.gitkeep</exclude>
+                                        <exclude>mime.types</exclude>
+                                        <exclude>mozilla/**</exclude>
+                                        <exclude>LICENSE.txt</exclude>
+                                        <exclude>license/LICENSE*</exclude>
+                                        
<exclude>okhttp3/internal/publicsuffix/NOTICE</exclude>
+                                        <exclude>NOTICE</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    
<artifact>org.apache.fluss:fluss-fs-hadoop</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/**</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <relocations>
+                                <relocation>
+                                    <pattern>org.apache.commons</pattern>
+                                    
<shadedPattern>org.apache.fluss.shaded.org.apache.commons</shadedPattern>
+                                </relocation>
+                            </relocations>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+
+</project>
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/COSFileSystem.java
 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/COSFileSystem.java
new file mode 100644
index 000000000..acb33e18d
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/COSFileSystem.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import org.apache.fluss.fs.cos.token.COSSecurityTokenProvider;
+import org.apache.fluss.fs.hdfs.HadoopFileSystem;
+import org.apache.fluss.fs.token.ObtainedSecurityToken;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * A {@link FileSystem} for Tencent Cloud COS that wraps an {@link 
HadoopFileSystem}, but overwrite
+ * method to generate access security token.
+ */
+class COSFileSystem extends HadoopFileSystem {
+
+    private final Configuration conf;
+    private volatile COSSecurityTokenProvider cosSecurityTokenProvider;
+    private final String scheme;
+    private final URI fsUri;
+
+    COSFileSystem(FileSystem hadoopFileSystem, String scheme, URI fsUri, 
Configuration conf) {
+        super(hadoopFileSystem);
+        this.scheme = scheme;
+        this.fsUri = fsUri;
+        this.conf = conf;
+    }
+
+    @Override
+    public ObtainedSecurityToken obtainSecurityToken() throws IOException {
+        try {
+            mayCreateSecurityTokenProvider();
+            return cosSecurityTokenProvider.obtainSecurityToken(scheme);
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+    private void mayCreateSecurityTokenProvider() throws IOException {
+        if (cosSecurityTokenProvider == null) {
+            synchronized (this) {
+                if (cosSecurityTokenProvider == null) {
+                    cosSecurityTokenProvider = new 
COSSecurityTokenProvider(fsUri, conf);
+                }
+            }
+        }
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/COSFileSystemPlugin.java
 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/COSFileSystemPlugin.java
new file mode 100644
index 000000000..95309eee6
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/COSFileSystemPlugin.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import org.apache.fluss.annotation.VisibleForTesting;
+import org.apache.fluss.config.ConfigBuilder;
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.fs.FileSystem;
+import org.apache.fluss.fs.FileSystemPlugin;
+import org.apache.fluss.fs.cos.token.COSSecurityTokenReceiver;
+
+import org.apache.hadoop.fs.cosn.CosNFileSystem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+
+/** Simple factory for the Tencent Cloud COS file system. */
+public class COSFileSystemPlugin implements FileSystemPlugin {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(COSFileSystemPlugin.class);
+
+    public static final String SCHEME = "cosn";
+
+    /**
+     * In order to simplify, we make fluss cos configuration keys same with 
hadoop cos module. So,
+     * we add all configuration key with prefix `fs.cosn` in fluss conf to 
hadoop conf.
+     */
+    private static final String[] FLUSS_CONFIG_PREFIXES = {"fs.cosn."};
+
+    public static final String SECRET_ID = "fs.cosn.userinfo.secretId";
+    public static final String SECRET_KEY = "fs.cosn.userinfo.secretKey";
+    public static final String CREDENTIALS_PROVIDER = 
"fs.cosn.credentials.provider";
+
+    public static final String REGION = "fs.cosn.userinfo.region";
+
+    public static final String ENDPOINT_KEY = "fs.cosn.bucket.endpoint_suffix";
+
+    /**
+     * Optional user-provided STS access policy (a JSON string) applied when 
calling {@code
+     * GetFederationToken}. When unset, Fluss will derive a default policy 
that scopes the temporary
+     * credential to the bucket configured in {@code remote.data.dir} so that 
the token cannot
+     * access other COS resources.
+     */
+    public static final String SECURITY_TOKEN_POLICY = 
"fs.cosn.security.token.policy";
+
+    @Override
+    public String getScheme() {
+        return SCHEME;
+    }
+
+    @Override
+    public FileSystem create(URI fsUri, Configuration flussConfig) throws 
IOException {
+        org.apache.hadoop.conf.Configuration hadoopConfig = 
getHadoopConfiguration(flussConfig);
+
+        // set credential provider
+        if (hadoopConfig.get(SECRET_ID) == null) {
+            String credentialsProvider = 
hadoopConfig.get(CREDENTIALS_PROVIDER);
+            if (credentialsProvider != null) {
+                LOG.info(
+                        "{} is not set, but {} is set, using credential 
provider {}.",
+                        SECRET_ID,
+                        CREDENTIALS_PROVIDER,
+                        credentialsProvider);
+            } else {
+                // no secretId, no credentialsProvider,
+                // set default credential provider which will get token from
+                // COSSecurityTokenReceiver
+                setDefaultCredentialProvider(hadoopConfig);
+            }
+        } else {
+            LOG.info("{} is set, using provided secret id and secret key.", 
SECRET_ID);
+        }
+
+        final String scheme = fsUri.getScheme();
+        final String authority = fsUri.getAuthority();
+
+        if (scheme == null && authority == null) {
+            fsUri = 
org.apache.hadoop.fs.FileSystem.getDefaultUri(hadoopConfig);
+        } else if (scheme != null && authority == null) {
+            URI defaultUri = 
org.apache.hadoop.fs.FileSystem.getDefaultUri(hadoopConfig);
+            if (scheme.equals(defaultUri.getScheme()) && 
defaultUri.getAuthority() != null) {
+                fsUri = defaultUri;
+            }
+        }
+
+        org.apache.hadoop.fs.FileSystem fileSystem = initFileSystem(fsUri, 
hadoopConfig);
+        return new COSFileSystem(fileSystem, getScheme(), fsUri, hadoopConfig);
+    }
+
+    protected org.apache.hadoop.fs.FileSystem initFileSystem(
+            URI fsUri, org.apache.hadoop.conf.Configuration hadoopConfig) 
throws IOException {
+        CosNFileSystem fileSystem = new CosNFileSystem();
+        fileSystem.initialize(fsUri, hadoopConfig);
+        return fileSystem;
+    }
+
+    protected void 
setDefaultCredentialProvider(org.apache.hadoop.conf.Configuration hadoopConfig) 
{
+        // use COSSecurityTokenReceiver to update hadoop config to set 
credentialsProvider
+        COSSecurityTokenReceiver.updateHadoopConfig(hadoopConfig);
+    }
+
+    @VisibleForTesting
+    org.apache.hadoop.conf.Configuration getHadoopConfiguration(Configuration 
flussConfig) {
+        org.apache.hadoop.conf.Configuration conf = new 
org.apache.hadoop.conf.Configuration();
+        if (flussConfig == null) {
+            return conf;
+        }
+
+        // read all configuration with prefix 'FLUSS_CONFIG_PREFIXES'
+        for (String key : flussConfig.keySet()) {
+            for (String prefix : FLUSS_CONFIG_PREFIXES) {
+                if (key.startsWith(prefix)) {
+                    String value =
+                            flussConfig.getString(
+                                    
ConfigBuilder.key(key).stringType().noDefaultValue(), null);
+                    conf.set(key, value);
+
+                    LOG.debug(
+                            "Adding Fluss config entry for {} as {} to Hadoop 
config",
+                            key,
+                            conf.get(key));
+                }
+            }
+        }
+        return conf;
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/COSSecurityTokenProvider.java
 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/COSSecurityTokenProvider.java
new file mode 100644
index 000000000..5b4390851
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/COSSecurityTokenProvider.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos.token;
+
+import org.apache.fluss.fs.token.Credentials;
+import org.apache.fluss.fs.token.CredentialsJsonSerde;
+import org.apache.fluss.fs.token.ObtainedSecurityToken;
+
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+import com.tencentcloudapi.sts.v20180813.StsClient;
+import com.tencentcloudapi.sts.v20180813.models.GetFederationTokenRequest;
+import com.tencentcloudapi.sts.v20180813.models.GetFederationTokenResponse;
+import org.apache.hadoop.conf.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.ENDPOINT_KEY;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.REGION;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.SECRET_ID;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.SECRET_KEY;
+import static 
org.apache.fluss.fs.cos.COSFileSystemPlugin.SECURITY_TOKEN_POLICY;
+import static org.apache.fluss.utils.Preconditions.checkNotNull;
+
+/**
+ * A provider to provide Tencent Cloud COS security token by calling STS 
GetFederationToken API to
+ * obtain temporary credentials.
+ */
+public class COSSecurityTokenProvider {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(COSSecurityTokenProvider.class);
+
+    /** Default federation token name. */
+    private static final String FEDERATION_TOKEN_NAME = "fluss-cos-federation";
+
+    /** Default duration seconds for temporary credentials, 3600s = 1h. */
+    private static final long DEFAULT_DURATION_SECONDS = 3600L;
+
+    private final String region;
+    private final String secretId;
+    private final String secretKey;
+    private final String policy;
+    private final Map<String, String> additionInfos;
+
+    public COSSecurityTokenProvider(URI fsUri, Configuration conf) {
+        checkNotNull(fsUri, "fsUri must not be null");
+        this.region = conf.get(REGION);
+        checkNotNull(region, "Region is not set. Please set " + REGION);
+        this.secretId = conf.get(SECRET_ID);
+        checkNotNull(secretId, "Secret ID is not set. Please set " + 
SECRET_ID);
+        this.secretKey = conf.get(SECRET_KEY);
+        checkNotNull(secretKey, "Secret Key is not set. Please set " + 
SECRET_KEY);
+        this.policy = resolvePolicy(conf, fsUri, region);
+
+        this.additionInfos = new HashMap<>();
+        for (String key : Arrays.asList(REGION, ENDPOINT_KEY)) {
+            if (conf.get(key) != null) {
+                additionInfos.put(key, conf.get(key));
+            }
+        }
+    }
+
+    public ObtainedSecurityToken obtainSecurityToken(String scheme)
+            throws TencentCloudSDKException {
+        LOG.info("Obtaining session credentials token with secret id: {}", 
secretId);
+
+        Credential cred = new Credential(secretId, secretKey);
+        StsClient stsClient = new StsClient(cred, region);
+
+        GetFederationTokenRequest request = new GetFederationTokenRequest();
+        request.setName(FEDERATION_TOKEN_NAME);
+        request.setDurationSeconds(DEFAULT_DURATION_SECONDS);
+        request.setPolicy(policy);
+
+        GetFederationTokenResponse response = 
stsClient.GetFederationToken(request);
+        com.tencentcloudapi.sts.v20180813.models.Credentials stsCredentials =
+                response.getCredentials();
+
+        // ExpiredTime is a Unix timestamp in seconds, convert to milliseconds
+        long expiredTimeMillis = response.getExpiredTime() * 1000L;
+
+        LOG.info(
+                "Session credentials obtained successfully with tmp secret id: 
{}, expiration: {}",
+                stsCredentials.getTmpSecretId(),
+                response.getExpiration());
+
+        return new ObtainedSecurityToken(
+                scheme, toJson(stsCredentials), expiredTimeMillis, 
additionInfos);
+    }
+
+    /**
+     * Resolves the STS policy to apply when calling {@code 
GetFederationToken}.
+     *
+     * <p>If the user has explicitly configured {@link
+     * org.apache.fluss.fs.cos.COSFileSystemPlugin#SECURITY_TOKEN_POLICY}, 
that value is used as-is.
+     * Otherwise a default policy is built that scopes the temporary 
credential to the bucket and
+     * (optional) prefix derived from the fsUri (i.e. {@code 
remote.data.dir}), so the token cannot
+     * be used to access other COS resources.
+     */
+    private static String resolvePolicy(Configuration conf, URI fsUri, String 
region) {
+        String userPolicy = conf.get(SECURITY_TOKEN_POLICY);
+        if (userPolicy != null && !userPolicy.trim().isEmpty()) {
+            LOG.info(
+                    "Using user-provided STS policy from {} for COS federation 
token.",
+                    SECURITY_TOKEN_POLICY);
+            return userPolicy;
+        }
+        return buildBucketScopedPolicy(fsUri, region);
+    }
+
+    /**
+     * Builds a default STS policy that grants {@code name/cos:*} only on the 
bucket (and optional
+     * key prefix) referenced by the given fsUri.
+     *
+     * <p>COS resource format used here: {@code 
qcs::cos:<region>:uid/*:<bucket>/<prefix>*}. The
+     * wildcard owner uid keeps the policy independent of the account uin 
while still restricting
+     * access to a specific bucket.
+     */
+    private static String buildBucketScopedPolicy(URI fsUri, String region) {
+        String bucket = fsUri.getAuthority();
+        if (bucket == null || bucket.isEmpty()) {
+            // Fall back to all-resources policy if we cannot derive the 
bucket from fsUri.
+            LOG.warn(
+                    "Unable to derive bucket from fsUri {}. Falling back to a 
wildcard COS resource"
+                            + " policy. Consider explicitly configuring {} to 
a least-privilege"
+                            + " policy.",
+                    fsUri,
+                    SECURITY_TOKEN_POLICY);
+            return "{"
+                    + "\"version\": \"2.0\","
+                    + "\"statement\": [{"
+                    + "\"action\": [\"name/cos:*\"],"
+                    + "\"effect\": \"allow\","
+                    + "\"resource\": [\"*\"]"
+                    + "}]"
+                    + "}";
+        }
+
+        String path = fsUri.getPath();
+        String prefix;
+        if (path == null || path.isEmpty() || "/".equals(path)) {
+            prefix = "";
+        } else {
+            // Strip leading '/' so the resource pattern looks like 
"<bucket>/sub/dir/*".
+            prefix = path.startsWith("/") ? path.substring(1) : path;
+            if (!prefix.endsWith("/")) {
+                prefix = prefix + "/";
+            }
+        }
+
+        String resource = "qcs::cos:" + region + ":uid/*:" + bucket + "/" + 
prefix + "*";
+        LOG.info("Using bucket-scoped STS policy with resource: {}", resource);
+        return "{"
+                + "\"version\": \"2.0\","
+                + "\"statement\": [{"
+                + "\"action\": [\"name/cos:*\"],"
+                + "\"effect\": \"allow\","
+                + "\"resource\": [\""
+                + resource
+                + "\"]"
+                + "}]"
+                + "}";
+    }
+
+    private byte[] toJson(com.tencentcloudapi.sts.v20180813.models.Credentials 
stsCredentials) {
+        Credentials credentials =
+                new Credentials(
+                        stsCredentials.getTmpSecretId(),
+                        stsCredentials.getTmpSecretKey(),
+                        stsCredentials.getToken());
+        return CredentialsJsonSerde.toJson(credentials);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/COSSecurityTokenReceiver.java
 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/COSSecurityTokenReceiver.java
new file mode 100644
index 000000000..130f375ed
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/COSSecurityTokenReceiver.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos.token;
+
+import org.apache.fluss.fs.cos.COSFileSystemPlugin;
+import org.apache.fluss.fs.token.CredentialsJsonSerde;
+import org.apache.fluss.fs.token.ObtainedSecurityToken;
+import org.apache.fluss.fs.token.SecurityTokenReceiver;
+
+import com.qcloud.cos.auth.BasicSessionCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.CREDENTIALS_PROVIDER;
+
+/** Security token receiver for Tencent Cloud COS filesystem. */
+public class COSSecurityTokenReceiver implements SecurityTokenReceiver {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(COSSecurityTokenReceiver.class);
+
+    static volatile COSCredentials credentials;
+    static volatile Map<String, String> additionInfos;
+
+    public static void updateHadoopConfig(org.apache.hadoop.conf.Configuration 
hadoopConfig) {
+        updateHadoopConfig(hadoopConfig, 
DynamicTemporaryCOSCredentialsProvider.NAME);
+    }
+
+    protected static void updateHadoopConfig(
+            org.apache.hadoop.conf.Configuration hadoopConfig, String 
credentialsProviderName) {
+        LOG.info("Updating Hadoop configuration");
+
+        String providers = hadoopConfig.get(CREDENTIALS_PROVIDER, "");
+
+        if (!providers.contains(credentialsProviderName)) {
+            if (providers.isEmpty()) {
+                LOG.debug("Setting provider");
+                providers = credentialsProviderName;
+            } else {
+                providers = credentialsProviderName + "," + providers;
+                LOG.debug("Prepending provider, new providers value: {}", 
providers);
+            }
+            hadoopConfig.set(CREDENTIALS_PROVIDER, providers);
+        } else {
+            LOG.debug("Provider already exists");
+        }
+
+        // then, set addition info
+        if (additionInfos == null) {
+            // if addition info is null, it also means we have not received 
any token,
+            throw new RuntimeException(
+                    "COS credentials have not been received yet. "
+                            + "Ensure onNewTokensObtained() has been called 
with valid tokens "
+                            + "before invoking 
COSSecurityTokenReceiver.updateHadoopConfig().");
+        } else {
+            for (Map.Entry<String, String> entry : additionInfos.entrySet()) {
+                hadoopConfig.set(entry.getKey(), entry.getValue());
+            }
+        }
+
+        LOG.info("Updated Hadoop configuration successfully");
+    }
+
+    @Override
+    public String scheme() {
+        return COSFileSystemPlugin.SCHEME;
+    }
+
+    @Override
+    public void onNewTokensObtained(ObtainedSecurityToken token) {
+        LOG.info("Updating session credentials");
+
+        byte[] tokenBytes = token.getToken();
+
+        org.apache.fluss.fs.token.Credentials flussCredentials =
+                CredentialsJsonSerde.fromJson(tokenBytes);
+
+        // STS always returns temporary credentials with a session token
+        credentials =
+                new BasicSessionCredentials(
+                        flussCredentials.getAccessKeyId(),
+                        flussCredentials.getSecretAccessKey(),
+                        flussCredentials.getSecurityToken());
+        additionInfos = token.getAdditionInfos();
+
+        // Consistent with S3DelegationTokenReceiver, logging access key at 
INFO level
+        LOG.info(
+                "Session credentials updated successfully with access key: 
{}.",
+                credentials.getCOSAccessKeyId());
+    }
+
+    public static COSCredentials getCredentials() {
+        return credentials;
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/DynamicTemporaryCOSCredentialsProvider.java
 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/DynamicTemporaryCOSCredentialsProvider.java
new file mode 100644
index 000000000..71d3e8af8
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/java/org/apache/fluss/fs/cos/token/DynamicTemporaryCOSCredentialsProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos.token;
+
+import org.apache.fluss.annotation.Internal;
+
+import com.qcloud.cos.auth.BasicSessionCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.auth.COSCredentialsProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Support dynamic credentials for authenticating with Tencent Cloud COS. 
It'll get credentials from
+ * {@link COSSecurityTokenReceiver}. It implements COS native {@link 
COSCredentialsProvider} to work
+ * with CosNFileSystem.
+ *
+ * <p>This provider supports both static credentials (secretId/secretKey) and 
temporary session
+ * credentials (secretId/secretKey/sessionToken).
+ */
+@Internal
+public class DynamicTemporaryCOSCredentialsProvider implements 
COSCredentialsProvider {
+
+    private static final Logger LOG =
+            
LoggerFactory.getLogger(DynamicTemporaryCOSCredentialsProvider.class);
+
+    public static final String NAME = 
DynamicTemporaryCOSCredentialsProvider.class.getName();
+
+    @Override
+    public COSCredentials getCredentials() {
+        COSCredentials credentials = COSSecurityTokenReceiver.getCredentials();
+        if (credentials == null) {
+            throw new RuntimeException(
+                    "COS credentials have not been received yet. "
+                            + "Ensure COSSecurityTokenReceiver has received 
valid tokens.");
+        }
+        if (credentials instanceof BasicSessionCredentials) {
+            BasicSessionCredentials sessionCredentials = 
(BasicSessionCredentials) credentials;
+            LOG.debug("Providing session credentials");
+            return new BasicSessionCredentials(
+                    sessionCredentials.getCOSAccessKeyId(),
+                    sessionCredentials.getCOSSecretKey(),
+                    sessionCredentials.getSessionToken());
+        } else {
+            LOG.debug("Providing non-session COS credentials");
+            return credentials;
+        }
+    }
+
+    @Override
+    public void refresh() {
+        // do nothing, credentials are updated by COSSecurityTokenReceiver
+    }
+}
diff --git a/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/NOTICE 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/NOTICE
new file mode 100644
index 000000000..feb58a9a2
--- /dev/null
+++ b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,77 @@
+fluss-fs-cos
+Copyright 2025-2026 The Apache Software Foundation
+
+This project includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This project bundles the following dependencies under the Apache Software 
License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+- com.fasterxml.jackson.core:jackson-annotations:2.15.3
+- com.fasterxml.jackson.core:jackson-core:2.15.3
+- com.fasterxml.jackson.core:jackson-databind:2.15.3
+- com.fasterxml.woodstox:woodstox-core:5.4.0
+- com.google.code.gson:gson:2.8.9
+- com.google.guava:failureaccess:1.0
+- com.google.guava:guava:27.0-jre
+- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
+- com.google.j2objc:j2objc-annotations:1.1
+- com.google.re2j:re2j:1.1
+- com.qcloud:cos_api:5.6.139
+- com.squareup.okhttp3:logging-interceptor:4.10.0
+- com.squareup.okhttp3:okhttp:4.10.0
+- com.squareup.okio:okio-jvm:3.2.0
+- com.squareup.okio:okio:3.2.0
+- com.tencentcloudapi:tencentcloud-sdk-java-common:3.1.678
+- com.tencentcloudapi:tencentcloud-sdk-java-kms:3.1.213
+- com.tencentcloudapi:tencentcloud-sdk-java-sts:3.1.678
+- commons-beanutils:commons-beanutils:1.9.4
+- commons-codec:commons-codec:1.13
+- commons-collections:commons-collections:3.2.2
+- commons-io:commons-io:2.14.0
+- commons-logging:commons-logging:1.2
+- dnsjava:dnsjava:3.4.0
+- io.dropwizard.metrics:metrics-core:3.2.4
+- io.netty:netty-buffer:4.1.100.Final
+- io.netty:netty-codec:4.1.100.Final
+- io.netty:netty-common:4.1.100.Final
+- io.netty:netty-handler:4.1.100.Final
+- io.netty:netty-resolver:4.1.100.Final
+- io.netty:netty-transport-classes-epoll:4.1.100.Final
+- io.netty:netty-transport-native-epoll:4.1.100.Final
+- io.netty:netty-transport-native-unix-common:4.1.100.Final
+- io.netty:netty-transport:4.1.100.Final
+- jakarta.activation:jakarta.activation-api:1.2.1
+- joda-time:joda-time:2.9.9
+- org.ini4j:ini4j:0.5.4
+- org.apache.commons:commons-compress:1.24.0
+- org.apache.commons:commons-configuration2:2.8.0
+- org.apache.commons:commons-lang3:3.18.0
+- org.apache.commons:commons-text:1.10.0
+- org.apache.hadoop:hadoop-annotations:3.4.0
+- org.apache.hadoop:hadoop-auth:3.4.0
+- org.apache.hadoop:hadoop-common:3.4.0
+- org.apache.hadoop:hadoop-cos:3.3.5
+- org.apache.hadoop.thirdparty:hadoop-shaded-guava:1.2.0
+- org.apache.hadoop.thirdparty:hadoop-shaded-protobuf_3_21:1.2.0
+- org.apache.httpcomponents:httpclient:4.5.13
+- org.apache.httpcomponents:httpcore:4.4.13
+- org.apache.kerby:kerb-core:2.0.3
+- org.apache.kerby:kerby-asn1:2.0.3
+- org.apache.kerby:kerby-pkix:2.0.3
+- org.apache.kerby:kerby-util:2.0.3
+- org.bouncycastle:bcprov-jdk15on:1.67
+- org.checkerframework:checker-qual:2.5.2
+- org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20
+- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10
+- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10
+- org.jetbrains.kotlin:kotlin-stdlib:1.6.20
+- org.jetbrains:annotations:13.0
+- org.codehaus.jettison:jettison:1.5.4
+- org.codehaus.mojo:animal-sniffer-annotations:1.17
+- org.codehaus.woodstox:stax2-api:4.2.1
+- org.xerial.snappy:snappy-java:1.1.10.4
+
+This project bundles the following dependencies under the CDDL 1.1 license.
+See bundled license files for details.
+
+- javax.xml.bind:jaxb-api:2.3.1
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/licenses/LICENSE.jaxb
 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/licenses/LICENSE.jaxb
new file mode 100644
index 000000000..fd16ea954
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/licenses/LICENSE.jaxb
@@ -0,0 +1,135 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)Version 1.1
+
+1. Definitions.
+
+     1.1. "Contributor" means each individual or entity that creates or 
contributes to the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original 
Software, prior Modifications used by a Contributor (if any), and the 
Modifications made by that particular Contributor.
+
+     1.3. "Covered Software" means (a) the Original Software, or (b) 
Modifications, or (c) the combination of files containing Original Software 
with files containing Modifications, in each case including portions thereof.
+
+     1.4. "Executable" means the Covered Software in any form other than 
Source Code.
+
+     1.5. "Initial Developer" means the individual or entity that first makes 
Original Software available under this License.
+
+     1.6. "Larger Work" means a work which combines Covered Software or 
portions thereof with code not governed by the terms of this License.
+
+     1.7. "License" means this document.
+
+     1.8. "Licensable" means having the right to grant, to the maximum extent 
possible, whether at the time of the initial grant or subsequently acquired, 
any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means the Source Code and Executable form of any of 
the following:
+
+     A. Any file that results from an addition to, deletion from or 
modification of the contents of a file containing Original Software or previous 
Modifications;
+
+     B. Any new file that contains any part of the Original Software or 
previous Modification; or
+
+     C. Any new file that is contributed or otherwise made available under the 
terms of this License.
+
+     1.10. "Original Software" means the Source Code and Executable form of 
computer software code that is originally released under this License.
+
+     1.11. "Patent Claims" means any patent claim(s), now owned or hereafter 
acquired, including without limitation, method, process, and apparatus claims, 
in any patent Licensable by grantor.
+
+     1.12. "Source Code" means (a) the common form of computer software code 
in which modifications are made and (b) associated documentation included in or 
with such code.
+
+     1.13. "You" (or "Your") means an individual or a legal entity exercising 
rights under, and complying with all of the terms of, this License. For legal 
entities, "You" includes any entity which controls, is controlled by, or is 
under common control with You. For purposes of this definition, "control" means 
(a) the power, direct or indirect, to cause the direction or management of such 
entity, whether by contract or otherwise, or (b) ownership of more than fifty 
percent (50%) of the o [...]
+
+2. License Grants.
+
+     2.1. The Initial Developer Grant.
+
+     Conditioned upon Your compliance with Section 3.1 below and subject to 
third party intellectual property claims, the Initial Developer hereby grants 
You a world-wide, royalty-free, non-exclusive license:
+
+     (a) under intellectual property rights (other than patent or trademark) 
Licensable by Initial Developer, to use, reproduce, modify, display, perform, 
sublicense and distribute the Original Software (or portions thereof), with or 
without Modifications, and/or as part of a Larger Work; and
+
+     (b) under Patent Claims infringed by the making, using or selling of 
Original Software, to make, have made, use, practice, sell, and offer for sale, 
and/or otherwise dispose of the Original Software (or portions thereof).
+
+     (c) The licenses granted in Sections 2.1(a) and (b) are effective on the 
date Initial Developer first distributes or otherwise makes the Original 
Software available to a third party under the terms of this License.
+
+     (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 
(1) for code that You delete from the Original Software, or (2) for 
infringements caused by: (i) the modification of the Original Software, or (ii) 
the combination of the Original Software with other software or devices.
+
+     2.2. Contributor Grant.
+
+     Conditioned upon Your compliance with Section 3.1 below and subject to 
third party intellectual property claims, each Contributor hereby grants You a 
world-wide, royalty-free, non-exclusive license:
+
+     (a) under intellectual property rights (other than patent or trademark) 
Licensable by Contributor to use, reproduce, modify, display, perform, 
sublicense and distribute the Modifications created by such Contributor (or 
portions thereof), either on an unmodified basis, with other Modifications, as 
Covered Software and/or as part of a Larger Work; and
+
+     (b) under Patent Claims infringed by the making, using, or selling of 
Modifications made by that Contributor either alone and/or in combination with 
its Contributor Version (or portions of such combination), to make, use, sell, 
offer for sale, have made, and/or otherwise dispose of: (1) Modifications made 
by that Contributor (or portions thereof); and (2) the combination of 
Modifications made by that Contributor with its Contributor Version (or 
portions of such combination).
+
+     (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on 
the date Contributor first distributes or otherwise makes the Modifications 
available to a third party.
+
+     (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 
(1) for any code that Contributor has deleted from the Contributor Version; (2) 
for infringements caused by: (i) third party modifications of Contributor 
Version, or (ii) the combination of Modifications made by that Contributor with 
other software (except as part of the Contributor Version) or other devices; or 
(3) under Patent Claims infringed by Covered Software in the absence of 
Modifications made by that Co [...]
+
+3. Distribution Obligations.
+
+     3.1. Availability of Source Code.
+
+     Any Covered Software that You distribute or otherwise make available in 
Executable form must also be made available in Source Code form and that Source 
Code form must be distributed only under the terms of this License. You must 
include a copy of this License with every copy of the Source Code form of the 
Covered Software You distribute or otherwise make available. You must inform 
recipients of any such Covered Software in Executable form as to how they can 
obtain such Covered Softw [...]
+
+     3.2. Modifications.
+
+     The Modifications that You create or to which You contribute are governed 
by the terms of this License. You represent that You believe Your Modifications 
are Your original creation(s) and/or You have sufficient rights to grant the 
rights conveyed by this License.
+
+     3.3. Required Notices.
+
+     You must include a notice in each of Your Modifications that identifies 
You as the Contributor of the Modification. You may not remove or alter any 
copyright, patent or trademark notices contained within the Covered Software, 
or any notices of licensing or any descriptive text giving attribution to any 
Contributor or the Initial Developer.
+
+     3.4. Application of Additional Terms.
+
+     You may not offer or impose any terms on any Covered Software in Source 
Code form that alters or restricts the applicable version of this License or 
the recipients' rights hereunder. You may choose to offer, and to charge a fee 
for, warranty, support, indemnity or liability obligations to one or more 
recipients of Covered Software. However, you may do so only on Your own behalf, 
and not on behalf of the Initial Developer or any Contributor. You must make it 
absolutely clear that any [...]
+
+     3.5. Distribution of Executable Versions.
+
+     You may distribute the Executable form of the Covered Software under the 
terms of this License or under the terms of a license of Your choice, which may 
contain terms different from this License, provided that You are in compliance 
with the terms of this License and that the license for the Executable form 
does not attempt to limit or alter the recipient's rights in the Source Code 
form from the rights set forth in this License. If You distribute the Covered 
Software in Executable f [...]
+
+     3.6. Larger Works.
+
+     You may create a Larger Work by combining Covered Software with other 
code not governed by the terms of this License and distribute the Larger Work 
as a single product. In such a case, You must make sure the requirements of 
this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+     4.1. New Versions.
+
+     Oracle is the initial license steward and may publish revised and/or new 
versions of this License from time to time. Each version will be given a 
distinguishing version number. Except as provided in Section 4.3, no one other 
than the license steward has the right to modify this License.
+
+     4.2. Effect of New Versions.
+
+     You may always continue to use, distribute or otherwise make the Covered 
Software available under the terms of the version of the License under which 
You originally received the Covered Software. If the Initial Developer includes 
a notice in the Original Software prohibiting it from being distributed or 
otherwise made available under any subsequent version of the License, You must 
distribute and make the Covered Software available under the terms of the 
version of the License under  [...]
+
+     4.3. Modified Versions.
+
+     When You are an Initial Developer and You want to create a new license 
for Your Original Software, You may create and use a modified version of this 
License if You: (a) rename the license and remove any references to the name of 
the license steward (except to note that the license differs from this 
License); and (b) otherwise make it clear that the license contains terms which 
differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+     COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT 
LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, 
MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK 
AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD 
ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL 
DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUM [...]
+
+6. TERMINATION.
+
+     6.1. This License and the rights granted hereunder will terminate 
automatically if You fail to comply with terms herein and fail to cure such 
breach within 30 days of becoming aware of the breach. Provisions which, by 
their nature, must remain in effect beyond the termination of this License 
shall survive.
+
+     6.2. If You assert a patent infringement claim (excluding declaratory 
judgment actions) against Initial Developer or a Contributor (the Initial 
Developer or Contributor against whom You assert such claim is referred to as 
"Participant") alleging that the Participant Software (meaning the Contributor 
Version where the Participant is a Contributor or the Original Software where 
the Participant is the Initial Developer) directly or indirectly infringes any 
patent, then any and all righ [...]
+
+     6.3. If You assert a patent infringement claim against Participant 
alleging that the Participant Software directly or indirectly infringes any 
patent where such claim is resolved (such as by license or settlement) prior to 
the initiation of patent infringement litigation, then the reasonable value of 
the licenses granted by such Participant under Sections 2.1 or 2.2 shall be 
taken into account in determining the amount or value of any payment or license.
+
+     6.4. In the event of termination under Sections 6.1 or 6.2 above, all end 
user licenses that have been validly granted by You or any distributor 
hereunder prior to termination (excluding licenses granted to You by any 
distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING 
NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY 
OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF 
ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, 
INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT 
LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR 
MALFUNCTION, OR ANY AND ALL OTHER COMM [...]
+
+8. U.S. GOVERNMENT END USERS.
+
+     The Covered Software is a "commercial item," as that term is defined in 
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as 
that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and "commercial 
computer software documentation" as such terms are used in 48 C.F.R. 12.212 
(Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 
227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software 
with only those rights set for [...]
+
+9. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject matter 
hereof. If any provision of this License is held to be unenforceable, such 
provision shall be reformed only to the extent necessary to make it 
enforceable. This License shall be governed by the law of the jurisdiction 
specified in a notice contained within the Original Software (except to the 
extent applicable law, if any, provides otherwise), excluding such 
jurisdiction's conflict-of-law provisions. Any litiga [...]
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is 
responsible for claims and damages arising, directly or indirectly, out of its 
utilization of rights under this License and You agree to work with Initial 
Developer and Contributors to distribute such responsibility on an equitable 
basis. Nothing herein is intended or shall be deemed to constitute any 
admission of liability.
+
+----------
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION 
LICENSE (CDDL)
+The code released under the CDDL shall be governed by the laws of the State of 
California (excluding conflict-of-law provisions). Any litigation relating to 
this License shall be subject to the jurisdiction of the Federal Courts of the 
Northern District of California and the state courts of the State of 
California, with venue lying in Santa Clara County, California.
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/services/org.apache.fluss.fs.FileSystemPlugin
 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/services/org.apache.fluss.fs.FileSystemPlugin
new file mode 100644
index 000000000..3919000bb
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/services/org.apache.fluss.fs.FileSystemPlugin
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+org.apache.fluss.fs.cos.COSFileSystemPlugin
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/services/org.apache.fluss.fs.token.SecurityTokenReceiver
 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/services/org.apache.fluss.fs.token.SecurityTokenReceiver
new file mode 100644
index 000000000..262c317c8
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/main/resources/META-INF/services/org.apache.fluss.fs.token.SecurityTokenReceiver
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+org.apache.fluss.fs.cos.token.COSSecurityTokenReceiver
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSFileSystemBehaviorITCase.java
 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSFileSystemBehaviorITCase.java
new file mode 100644
index 000000000..0e00f9f59
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSFileSystemBehaviorITCase.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.fs.FileSystem;
+import org.apache.fluss.fs.FileSystemBehaviorTestSuite;
+import org.apache.fluss.fs.FsPath;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.UUID;
+
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.ENDPOINT_KEY;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.SECRET_ID;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.SECRET_KEY;
+
+/**
+ * An implementation of the {@link FileSystemBehaviorTestSuite} for the COS 
file system with Hadoop
+ * cos sdk.
+ */
+class COSFileSystemBehaviorITCase extends FileSystemBehaviorTestSuite {
+
+    private static final String TEST_DATA_DIR = "tests-" + UUID.randomUUID();
+
+    @BeforeAll
+    static void setup() {
+        COSTestCredentials.assumeCredentialsAvailable();
+
+        final Configuration conf = new Configuration();
+        conf.setString(ENDPOINT_KEY, 
COSTestCredentials.getCOSEndpointSuffix());
+        conf.setString(SECRET_ID, COSTestCredentials.getCOSSecretId());
+        conf.setString(SECRET_KEY, COSTestCredentials.getCOSSecretKey());
+        FileSystem.initialize(conf, null);
+    }
+
+    @Override
+    protected FileSystem getFileSystem() throws Exception {
+        return getBasePath().getFileSystem();
+    }
+
+    @Override
+    protected FsPath getBasePath() {
+        return new FsPath(COSTestCredentials.getTestBucketUri() + 
TEST_DATA_DIR);
+    }
+
+    @AfterAll
+    static void clearFsConfig() {
+        FileSystem.initialize(new Configuration(), null);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSTestCredentials.java
 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSTestCredentials.java
new file mode 100644
index 000000000..854f4c445
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSTestCredentials.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import javax.annotation.Nullable;
+
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/** Access to credentials to access COS buckets during integration tests. */
+public class COSTestCredentials {
+    @Nullable private static final String ENDPOINT_SUFFIX = 
System.getenv("COSN_ENDPOINT_SUFFIX");
+
+    @Nullable private static final String BUCKET = 
System.getenv("COSN_BUCKET");
+
+    @Nullable private static final String SECRET_ID = 
System.getenv("COSN_SECRET_ID");
+
+    @Nullable private static final String SECRET_KEY = 
System.getenv("COSN_SECRET_KEY");
+
+    @Nullable private static final String REGION = 
System.getenv("COSN_REGION");
+
+    // ------------------------------------------------------------------------
+
+    public static boolean credentialsAvailable() {
+        return isNotEmpty(ENDPOINT_SUFFIX)
+                && isNotEmpty(BUCKET)
+                && isNotEmpty(SECRET_ID)
+                && isNotEmpty(SECRET_KEY)
+                && isNotEmpty(REGION);
+    }
+
+    /** Checks if a String is not null and not empty. */
+    private static boolean isNotEmpty(@Nullable String str) {
+        return str != null && !str.isEmpty();
+    }
+
+    public static void assumeCredentialsAvailable() {
+        assumeTrue(
+                credentialsAvailable(), "No COS credentials available in this 
test's environment");
+    }
+
+    /**
+     * Get COS endpoint suffix used to connect.
+     *
+     * @return COS endpoint suffix
+     */
+    public static String getCOSEndpointSuffix() {
+        if (ENDPOINT_SUFFIX != null) {
+            return ENDPOINT_SUFFIX;
+        } else {
+            throw new IllegalStateException("COS endpoint suffix is not 
available");
+        }
+    }
+
+    /**
+     * Get COS secret id.
+     *
+     * @return COS secret id
+     */
+    public static String getCOSSecretId() {
+        if (SECRET_ID != null) {
+            return SECRET_ID;
+        } else {
+            throw new IllegalStateException("COS secret id is not available");
+        }
+    }
+
+    /**
+     * Get COS secret key.
+     *
+     * @return COS secret key
+     */
+    public static String getCOSSecretKey() {
+        if (SECRET_KEY != null) {
+            return SECRET_KEY;
+        } else {
+            throw new IllegalStateException("COS secret key is not available");
+        }
+    }
+
+    /**
+     * Get COS region.
+     *
+     * @return COS region
+     */
+    public static String getCOSRegion() {
+        if (REGION != null) {
+            return REGION;
+        } else {
+            throw new IllegalStateException("COS region is not available");
+        }
+    }
+
+    public static String getTestBucketUri() {
+        return getTestBucketUriWithScheme("cosn");
+    }
+
+    public static String getTestBucketUriWithScheme(String scheme) {
+        if (BUCKET != null) {
+            return scheme + "://" + BUCKET + "/";
+        } else {
+            throw new IllegalStateException("COS test bucket is not 
available");
+        }
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithCredentialsProviderFileSystemBehaviorITCase.java
 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithCredentialsProviderFileSystemBehaviorITCase.java
new file mode 100644
index 000000000..9582ae560
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithCredentialsProviderFileSystemBehaviorITCase.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.fs.FileSystem;
+import org.apache.fluss.fs.FileSystemBehaviorTestSuite;
+import org.apache.fluss.fs.FsPath;
+
+import com.qcloud.cos.auth.COSCredentialsProvider;
+import org.apache.hadoop.fs.cosn.auth.EnvironmentVariableCredentialsProvider;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.UUID;
+
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.CREDENTIALS_PROVIDER;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.ENDPOINT_KEY;
+
+/** IT case for access cos via set {@link COSCredentialsProvider}. */
+class COSWithCredentialsProviderFileSystemBehaviorITCase extends 
FileSystemBehaviorTestSuite {
+
+    private static final String TEST_DATA_DIR = "tests-" + UUID.randomUUID();
+
+    @BeforeAll
+    static void setup() {
+        COSTestCredentials.assumeCredentialsAvailable();
+
+        // Use EnvironmentVariableCredentialsProvider shipped with hadoop-cos, 
which reads
+        // credentials from the COSN_SECRET_ID / COSN_SECRET_KEY environment 
variables
+        // (the same ones already required by COSTestCredentials).
+        final Configuration conf = new Configuration();
+        conf.setString(ENDPOINT_KEY, 
COSTestCredentials.getCOSEndpointSuffix());
+        conf.setString(
+                CREDENTIALS_PROVIDER, 
EnvironmentVariableCredentialsProvider.class.getName());
+        FileSystem.initialize(conf, null);
+    }
+
+    @Override
+    protected FileSystem getFileSystem() throws Exception {
+        return getBasePath().getFileSystem();
+    }
+
+    @Override
+    protected FsPath getBasePath() {
+        return new FsPath(COSTestCredentials.getTestBucketUri() + 
TEST_DATA_DIR);
+    }
+
+    @AfterAll
+    static void clearFsConfig() {
+        FileSystem.initialize(new Configuration(), null);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithTokenFileSystemBehaviorBaseITCase.java
 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithTokenFileSystemBehaviorBaseITCase.java
new file mode 100644
index 000000000..46586c0f6
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithTokenFileSystemBehaviorBaseITCase.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.fs.FileSystem;
+import org.apache.fluss.fs.FileSystemBehaviorTestSuite;
+
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.ENDPOINT_KEY;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.REGION;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.SECRET_ID;
+import static org.apache.fluss.fs.cos.COSFileSystemPlugin.SECRET_KEY;
+
+/** Base IT case for access COS with temporary credentials in hadoop sdk as 
COS FileSystem. */
+abstract class COSWithTokenFileSystemBehaviorBaseITCase extends 
FileSystemBehaviorTestSuite {
+
+    static void initFileSystemWithSecretKey() {
+        COSTestCredentials.assumeCredentialsAvailable();
+
+        // first init filesystem with secretId/secretKey
+        final Configuration conf = new Configuration();
+        conf.setString(ENDPOINT_KEY, 
COSTestCredentials.getCOSEndpointSuffix());
+        conf.setString(REGION, COSTestCredentials.getCOSRegion());
+        conf.setString(SECRET_ID, COSTestCredentials.getCOSSecretId());
+        conf.setString(SECRET_KEY, COSTestCredentials.getCOSSecretKey());
+        FileSystem.initialize(conf, null);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithTokenFileSystemBehaviorITCase.java
 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithTokenFileSystemBehaviorITCase.java
new file mode 100644
index 000000000..c5dd30c86
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-cos/src/test/java/org/apache/fluss/fs/cos/COSWithTokenFileSystemBehaviorITCase.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.fluss.fs.cos;
+
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.fs.FileSystem;
+import org.apache.fluss.fs.FsPath;
+import org.apache.fluss.fs.cos.token.COSSecurityTokenReceiver;
+import org.apache.fluss.fs.token.ObtainedSecurityToken;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.UUID;
+
+/** IT case for access COS with security token in hadoop sdk as FileSystem. */
+class COSWithTokenFileSystemBehaviorITCase extends 
COSWithTokenFileSystemBehaviorBaseITCase {
+
+    private static final String TEST_DATA_DIR = "tests-" + UUID.randomUUID();
+
+    @BeforeAll
+    static void setup() throws Exception {
+        // init a filesystem with secretId/secretKey so that it can generate 
token
+        initFileSystemWithSecretKey();
+        // now, we can init with token
+        initFileSystemWithToken(getFsPath());
+    }
+
+    @Override
+    protected FileSystem getFileSystem() throws Exception {
+        return getFsPath().getFileSystem();
+    }
+
+    @Override
+    protected FsPath getBasePath() {
+        return getFsPath();
+    }
+
+    protected static FsPath getFsPath() {
+        return new FsPath(COSTestCredentials.getTestBucketUri() + 
TEST_DATA_DIR);
+    }
+
+    @AfterAll
+    static void clearFsConfig() {
+        FileSystem.initialize(new Configuration(), null);
+    }
+
+    private static void initFileSystemWithToken(FsPath fsPath) throws 
Exception {
+        Configuration configuration = new Configuration();
+        // obtain a security token and call onNewTokensObtained
+        ObtainedSecurityToken obtainedSecurityToken = 
fsPath.getFileSystem().obtainSecurityToken();
+        COSSecurityTokenReceiver cosSecurityTokenReceiver = new 
COSSecurityTokenReceiver();
+        cosSecurityTokenReceiver.onNewTokensObtained(obtainedSecurityToken);
+
+        FileSystem.initialize(configuration, null);
+    }
+}
diff --git a/fluss-filesystems/pom.xml b/fluss-filesystems/pom.xml
index a3bafb480..34a81de98 100644
--- a/fluss-filesystems/pom.xml
+++ b/fluss-filesystems/pom.xml
@@ -36,6 +36,7 @@
         <module>fluss-fs-gs</module>
         <module>fluss-fs-azure</module>
         <module>fluss-fs-obs</module>
+        <module>fluss-fs-cos</module>
         <module>fluss-fs-hdfs</module>
     </modules>
     <packaging>pom</packaging>
diff --git a/fluss-test-coverage/pom.xml b/fluss-test-coverage/pom.xml
index 2f9fdba58..944378b00 100644
--- a/fluss-test-coverage/pom.xml
+++ b/fluss-test-coverage/pom.xml
@@ -429,6 +429,7 @@
                                         </exclude>
                                         
<exclude>org.apache.fluss.fs.hdfs.HdfsSecurityTokenReceiver</exclude>
                                         
<exclude>org.apache.fluss.fs.oss.*</exclude>
+                                        
<exclude>org.apache.fluss.fs.cos.*</exclude>
                                         
<exclude>org.apache.fluss.fs.s3.*</exclude>
                                         
<exclude>org.apache.fluss.fs.obs.*</exclude>
                                         
<exclude>com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser*
diff --git a/website/docs/maintenance/filesystems/cos.md 
b/website/docs/maintenance/filesystems/cos.md
new file mode 100644
index 000000000..192c4cbd3
--- /dev/null
+++ b/website/docs/maintenance/filesystems/cos.md
@@ -0,0 +1,89 @@
+---
+title: Tencent Cloud COS
+sidebar_position: 7
+---
+
+# Tencent Cloud COS
+
+[Tencent Cloud Object Storage](https://cloud.tencent.com/product/cos) (Tencent 
Cloud COS) is a distributed storage service offered by Tencent Cloud for 
storing massive amounts of data. It provides high scalability, low cost, 
reliability, and security.
+
+## Install COS Plugin Manually
+
+Tencent Cloud COS support is not included in the default Fluss distribution. 
To enable COS support, you need to manually install the filesystem plugin into 
Fluss.
+
+1. **Prepare the plugin JAR**:
+
+   - Download the `fluss-fs-cos-$FLUSS_VERSION$.jar` from the [Maven 
Repository](https://repo1.maven.org/maven2/org/apache/fluss/fluss-fs-cos/$FLUSS_VERSION$/fluss-fs-cos-$FLUSS_VERSION$.jar).
+
+2. **Place the plugin**: Place the plugin JAR file in the 
`${FLUSS_HOME}/plugins/cos/` directory:
+   ```bash
+   mkdir -p ${FLUSS_HOME}/plugins/cos/
+   cp fluss-fs-cos-$FLUSS_VERSION$.jar ${FLUSS_HOME}/plugins/cos/
+   ```
+
+3. Restart Fluss if the cluster is already running to ensure the new plugin is 
loaded.
+
+## Configurations setup
+
+To enable Tencent Cloud COS as remote storage, there are some required 
configurations that must be added to Fluss' `server.yaml`:
+
+```yaml
+# The dir that used to be as the remote storage of Fluss
+remote.data.dir: cosn://<your-bucket>/path/to/remote/storage
+# COS endpoint suffix, such as: cos.ap-guangzhou.myqcloud.com
+fs.cosn.bucket.endpoint_suffix: <your-endpoint-suffix>
+# COS region, such as: ap-guangzhou
+fs.cosn.userinfo.region: <your-cos-region>
+
+# Authentication (choose one option below)
+
+# Option 1: Direct credentials
+# Tencent Cloud secret id
+fs.cosn.userinfo.secretId: <your-secret-id>
+# Tencent Cloud secret key
+fs.cosn.userinfo.secretKey: <your-secret-key>
+
+# Option 2: Secure credential provider
+fs.cosn.credentials.provider: <your-credentials-provider>
+```
+To avoid exposing sensitive access key information directly in the 
`server.yaml`, you can choose option2 to use a credential provider by setting 
the `fs.cosn.credentials.provider` property.
+
+For example, to use environment variables for credential management:
+```yaml
+fs.cosn.credentials.provider: 
org.apache.hadoop.fs.cosn.auth.EnvironmentVariableCredentialsProvider
+```
+Then, set the following environment variables before starting the Fluss 
service:
+```bash
+export COSN_SECRET_ID=<your-secret-id>
+export COSN_SECRET_KEY=<your-secret-key>
+```
+This approach enhances security by keeping sensitive credentials out of 
configuration files.
+
+## Token-based Authentication
+
+For client to access the remote storage such as reading snapshot or tiered 
log, client must obtain a temporary credential (STS token) from Fluss cluster.
+
+Fluss uses Tencent Cloud STS 
[GetFederationToken](https://cloud.tencent.com/document/product/1312/48195) API 
to obtain temporary credentials.
+Unlike `AssumeRole`, `GetFederationToken` does not require a role ARN — it 
generates temporary credentials directly from the caller's permanent 
credentials (`secretId` / `secretKey`).
+
+To enable this, you must configure `fs.cosn.userinfo.region` which is required 
for calling the STS API.
+The `fs.cosn.userinfo.region` specifies the COS region where your bucket is 
located, such as `ap-guangzhou`, `ap-beijing`, etc. You can
+find different regions in [Tencent Cloud COS 
Regions](https://cloud.tencent.com/document/product/436/6224).
+
+### How it works
+
+1. The Fluss server calls `GetFederationToken` with the configured permanent 
credentials to obtain a temporary credential consisting of a temporary secret 
id, a temporary secret key, and a session token.
+2. By default, the temporary credential is granted **full COS read-write 
permission** (`name/cos:*`) scoped to the bucket configured via 
`remote.data.dir`, and has a **validity period of 1 hour**. You can override 
this default policy by setting `fs.cosn.security.token.policy` to a custom STS 
policy JSON.
+3. The temporary credential is distributed to clients, which use it to access 
COS directly.
+4. Fluss automatically refreshes the credential before it expires.
+
+:::note
+The effective permission of the temporary credential is the **intersection** 
of the permanent credential's permission and the policy specified in the 
`GetFederationToken` request.
+So the permanent credential (the `secretId` / `secretKey` configured in 
`server.yaml`) must have sufficient COS permissions.
+See more details in [Tencent Cloud COS Access 
Policy](https://cloud.tencent.com/document/product/436/6884).
+:::
+
+## Advanced Configurations
+
+Apart from the above configurations, you can also define the configuration 
keys mentioned in the [Hadoop-COS 
documentation](https://hadoop.apache.org/docs/current/hadoop-cos/cloud-storage/index.html)
+in the Fluss' `server.yaml`. These configurations defined in Hadoop-COS 
documentation are advanced configurations which are usually used by performance 
tuning.
diff --git a/website/docs/maintenance/filesystems/overview.md 
b/website/docs/maintenance/filesystems/overview.md
index 17e4adeb4..c02abbda6 100644
--- a/website/docs/maintenance/filesystems/overview.md
+++ b/website/docs/maintenance/filesystems/overview.md
@@ -7,7 +7,7 @@ sidebar_position: 1
 # File Systems
 
 Fluss uses file systems as remote storage to store snapshots for Primary-Key 
Table and store tiered log segments for Log Table. These
-are some of the file systems that Fluss supports currently, including *local*, 
*hadoop*, *Aliyun OSS*.
+are some of the file systems that Fluss supports currently, including *local*, 
*hadoop*, *Aliyun OSS*, *Tencent Cloud COS*.
 
 The file system used for a particular file is determined by its URI scheme. 
For example, `file:///home/user/text.txt` refers to a file in the local file 
system,
 while `hdfs://namenode:50010/data/user/text.txt` is a file in a specific HDFS 
cluster.
@@ -40,4 +40,6 @@ The Fluss project supports the following file systems:
 
 - **[HuaweiCloud OBS](obs.md)** is supported by `fluss-fs-obs` and registered 
under the `obs://` URI scheme. Please make sure to [manually install the OBS 
plugin](obs.md#install-obs-plugin-manually).
 
+- **[Tencent Cloud COS](cos.md)** is supported by `fluss-fs-cos` and 
registered under the `cosn://` URI scheme. Please make sure to [manually 
install the COS plugin](cos.md#install-cos-plugin-manually).
+
 The implementation is based on [Hadoop Project](https://hadoop.apache.org/) 
but is self-contained with no dependency footprint.
\ No newline at end of file

Reply via email to